vinext 1.0.0-beta.2 → 1.0.0-beta.3

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 (126) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/check.js +4 -0
  4. package/dist/config/config-matchers.js +4 -1
  5. package/dist/config/next-config.d.ts +12 -3
  6. package/dist/config/next-config.js +4 -0
  7. package/dist/entries/app-browser-entry.d.ts +4 -10
  8. package/dist/entries/app-browser-entry.js +20 -4
  9. package/dist/entries/app-rsc-entry.js +3 -0
  10. package/dist/entries/app-rsc-manifest.js +19 -0
  11. package/dist/entries/pages-client-entry.js +0 -1
  12. package/dist/entries/pages-server-entry.js +10 -3
  13. package/dist/index.js +112 -43
  14. package/dist/init-cloudflare.d.ts +3 -1
  15. package/dist/init-cloudflare.js +59 -5
  16. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  17. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  18. package/dist/plugins/import-meta-url.js +17 -6
  19. package/dist/plugins/require-context.js +20 -15
  20. package/dist/plugins/transform-cache.d.ts +19 -0
  21. package/dist/plugins/transform-cache.js +36 -0
  22. package/dist/routing/app-route-graph.d.ts +14 -6
  23. package/dist/routing/app-route-graph.js +106 -16
  24. package/dist/server/api-handler.d.ts +2 -0
  25. package/dist/server/api-handler.js +3 -3
  26. package/dist/server/app-browser-entry.js +103 -31
  27. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  28. package/dist/server/app-browser-navigation-controller.js +1 -1
  29. package/dist/server/app-page-cache-finalizer.js +1 -1
  30. package/dist/server/app-page-cache.d.ts +1 -1
  31. package/dist/server/app-page-cache.js +16 -2
  32. package/dist/server/app-page-dispatch.d.ts +8 -0
  33. package/dist/server/app-page-dispatch.js +31 -6
  34. package/dist/server/app-page-element-builder.d.ts +2 -0
  35. package/dist/server/app-page-element-builder.js +33 -7
  36. package/dist/server/app-page-head.d.ts +3 -3
  37. package/dist/server/app-page-head.js +58 -13
  38. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  39. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  40. package/dist/server/app-page-probe.d.ts +4 -0
  41. package/dist/server/app-page-probe.js +6 -2
  42. package/dist/server/app-page-request.d.ts +3 -0
  43. package/dist/server/app-page-request.js +1 -1
  44. package/dist/server/app-page-response.js +1 -1
  45. package/dist/server/app-page-route-wiring.d.ts +7 -0
  46. package/dist/server/app-page-route-wiring.js +211 -49
  47. package/dist/server/app-pages-bridge.d.ts +1 -1
  48. package/dist/server/app-pages-bridge.js +2 -1
  49. package/dist/server/app-route-handler-cache.js +4 -0
  50. package/dist/server/app-route-handler-dispatch.js +9 -3
  51. package/dist/server/app-route-handler-response.js +1 -1
  52. package/dist/server/app-route-module-loader.d.ts +6 -0
  53. package/dist/server/app-route-module-loader.js +3 -0
  54. package/dist/server/app-router-entry.js +6 -3
  55. package/dist/server/app-rsc-errors.js +10 -0
  56. package/dist/server/app-rsc-handler.js +21 -9
  57. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  58. package/dist/server/app-rsc-route-matching.js +6 -0
  59. package/dist/server/app-ssr-entry.js +4 -4
  60. package/dist/server/cache-control.d.ts +2 -2
  61. package/dist/server/cache-control.js +2 -1
  62. package/dist/server/dev-server.d.ts +3 -1
  63. package/dist/server/dev-server.js +145 -312
  64. package/dist/server/headers.d.ts +2 -2
  65. package/dist/server/headers.js +4 -3
  66. package/dist/server/isr-cache.d.ts +10 -33
  67. package/dist/server/isr-cache.js +33 -43
  68. package/dist/server/isr-decision.d.ts +2 -2
  69. package/dist/server/pages-api-route.d.ts +2 -0
  70. package/dist/server/pages-api-route.js +4 -1
  71. package/dist/server/pages-dev-hydration.js +0 -1
  72. package/dist/server/pages-get-initial-props.d.ts +1 -1
  73. package/dist/server/pages-get-initial-props.js +1 -4
  74. package/dist/server/pages-i18n.js +64 -16
  75. package/dist/server/pages-node-compat.d.ts +2 -0
  76. package/dist/server/pages-node-compat.js +7 -3
  77. package/dist/server/pages-page-data.d.ts +61 -9
  78. package/dist/server/pages-page-data.js +334 -74
  79. package/dist/server/pages-page-handler.d.ts +5 -1
  80. package/dist/server/pages-page-handler.js +115 -19
  81. package/dist/server/pages-page-response.d.ts +5 -3
  82. package/dist/server/pages-page-response.js +23 -26
  83. package/dist/server/pages-request-pipeline.d.ts +7 -0
  84. package/dist/server/pages-request-pipeline.js +5 -1
  85. package/dist/server/pages-revalidate.d.ts +1 -1
  86. package/dist/server/pages-revalidate.js +54 -8
  87. package/dist/server/pages-router-entry.d.ts +1 -0
  88. package/dist/server/pages-router-entry.js +10 -4
  89. package/dist/server/prod-server.d.ts +2 -1
  90. package/dist/server/prod-server.js +37 -13
  91. package/dist/server/revalidation-host.d.ts +11 -0
  92. package/dist/server/revalidation-host.js +19 -0
  93. package/dist/server/worker-revalidation-context.d.ts +13 -0
  94. package/dist/server/worker-revalidation-context.js +29 -0
  95. package/dist/shims/cache-handler.d.ts +1 -1
  96. package/dist/shims/cache-handler.js +10 -7
  97. package/dist/shims/cache-runtime.d.ts +6 -0
  98. package/dist/shims/cache-runtime.js +4 -1
  99. package/dist/shims/cache.js +1 -1
  100. package/dist/shims/error.js +1 -1
  101. package/dist/shims/fetch-cache.js +1 -1
  102. package/dist/shims/font-google-base.js +17 -8
  103. package/dist/shims/font-local.js +41 -10
  104. package/dist/shims/headers.d.ts +10 -13
  105. package/dist/shims/headers.js +85 -36
  106. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  107. package/dist/shims/internal/cookie-serialize.js +9 -8
  108. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  109. package/dist/shims/internal/interpolate-as.js +27 -2
  110. package/dist/shims/link.js +18 -6
  111. package/dist/shims/metadata.d.ts +7 -17
  112. package/dist/shims/metadata.js +74 -24
  113. package/dist/shims/navigation.js +1 -1
  114. package/dist/shims/request-context.d.ts +4 -1
  115. package/dist/shims/router.js +18 -7
  116. package/dist/shims/script.js +1 -1
  117. package/dist/shims/server.d.ts +7 -6
  118. package/dist/shims/server.js +156 -57
  119. package/dist/shims/unified-request-context.d.ts +21 -2
  120. package/dist/shims/unified-request-context.js +82 -1
  121. package/dist/utils/cache-control-metadata.d.ts +2 -1
  122. package/dist/utils/cache-control-metadata.js +5 -1
  123. package/dist/utils/html-limited-bots.js +1 -1
  124. package/dist/utils/protocol-headers.d.ts +7 -1
  125. package/dist/utils/protocol-headers.js +7 -1
  126. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ //#region src/plugins/transform-cache.ts
2
+ /**
3
+ * Memoizes a pure per-module transform across the repeated build passes
4
+ * (scan client/server references + rsc/client/ssr builds), which feed the
5
+ * same module source through the same transform up to five times.
6
+ *
7
+ * Entries are keyed by module id and replaced when the source changes, so dev
8
+ * rebuilds retain at most one source version per id. `variant` distinguishes
9
+ * results that also depend on an environment-derived input (e.g. the
10
+ * typeof-window replacement); callers must pass a primitive or stable
11
+ * reference. Cached values include `null` ("no transform applies") so the
12
+ * negative case is not recomputed either.
13
+ *
14
+ * Only safe for transforms whose result depends solely on (id, source,
15
+ * variant) — no filesystem or plugin-state reads inside `compute`.
16
+ */
17
+ function createTransformCache() {
18
+ const cache = /* @__PURE__ */ new Map();
19
+ return (id, source, variant, compute) => {
20
+ let entry = cache.get(id);
21
+ if (entry?.source !== source) {
22
+ entry = {
23
+ source,
24
+ results: /* @__PURE__ */ new Map()
25
+ };
26
+ cache.set(id, entry);
27
+ }
28
+ const boxed = entry.results.get(variant);
29
+ if (boxed) return boxed.value;
30
+ const value = compute();
31
+ entry.results.set(variant, { value });
32
+ return value;
33
+ };
34
+ }
35
+ //#endregion
36
+ export { createTransformCache };
@@ -24,7 +24,9 @@ type InterceptingRoute = {
24
24
  pagePath: string; /** Filesystem segments from app/ root to the intercepting page directory. */
25
25
  sourcePageSegments?: string[]; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
26
26
  layoutPaths: string[]; /** Normalized branch segments accumulated at each intercept layout. */
27
- layoutSegments?: string[][]; /** Full normalized interception branch segments through the page. */
27
+ layoutSegments?: string[][]; /** Loading conventions inside the intercepted branch, outermost to innermost. */
28
+ loadingPaths?: string[]; /** Tree positions for loadingPaths in the normalized interception branch. */
29
+ loadingTreePositions?: number[]; /** Full normalized interception branch segments through the page. */
28
30
  branchSegments?: string[]; /** Nearest not-found convention inside the interception branch. */
29
31
  notFoundPath?: string | null; /** Slot-root-relative branch segments used to scope not-found params. */
30
32
  notFoundBranchSegments?: string[]; /** Tree position relative to the normalized interception branch. */
@@ -41,14 +43,17 @@ type ParallelSlot = {
41
43
  key: string; /** Slot name (e.g. "team" from @team) */
42
44
  name: string; /** Absolute path to the @slot directory that owns this slot. Internal routing metadata. */
43
45
  ownerDir: string; /** Stable tree path for the directory whose layout owns this slot. */
44
- ownerTreePath: string; /** Whether the slot owner directory declares its own page component. */
46
+ ownerTreePath: string; /** Directory depth of the slot owner from app/ root. */
47
+ ownerTreePosition?: number; /** Whether the slot owner directory declares its own page component. */
45
48
  hasPage: boolean; /** Absolute path to the slot's page component */
46
49
  pagePath: string | null; /** Absolute path to the slot's default.tsx fallback */
47
50
  defaultPath: string | null; /** Absolute path to the slot's layout component (wraps slot content) */
48
51
  layoutPath: string | null; /** Nested active-branch layouts whose exports contribute route config. */
49
52
  configLayoutPaths?: string[]; /** Tree positions of configLayoutPaths relative to the slot root. */
50
53
  configLayoutTreePositions?: number[]; /** Absolute path to the slot's loading component */
51
- loadingPath: string | null; /** Absolute path to the slot's error component */
54
+ loadingPath: string | null; /** Per-segment loading components from the slot root to its active page. */
55
+ loadingPaths?: string[]; /** Slot-root-relative tree positions aligned with loadingPaths. */
56
+ loadingTreePositions?: number[]; /** Absolute path to the slot's error component */
52
57
  errorPath: string | null; /** Nearest not-found convention for the slot's active branch. */
53
58
  notFoundPath?: string | null; /** Tree position of that convention relative to the slot root. */
54
59
  notFoundTreePosition?: number | null; /** Intercepting routes within this slot */
@@ -99,8 +104,10 @@ type AppRoute = {
99
104
  * On soft-nav, the intercepting page replaces the entire page response.
100
105
  * Empty array when there are no sibling-style interception markers.
101
106
  */
102
- siblingIntercepts: InterceptingRoute[]; /** Loading component path */
103
- loadingPath: string | null; /** Error component path (leaf directory only) */
107
+ siblingIntercepts: InterceptingRoute[]; /** Loading component path (leaf directory only) */
108
+ loadingPath: string | null; /** Per-segment loading component paths, aligned with loadingTreePositions. */
109
+ loadingPaths?: string[]; /** Tree position (directory depth from app/ root) for each loading boundary. */
110
+ loadingTreePositions?: number[]; /** Error component path (leaf directory only) */
104
111
  errorPath: string | null;
105
112
  /**
106
113
  * Per-layout error boundary paths, aligned with the layouts array.
@@ -306,6 +313,7 @@ declare function buildAppRouteGraph(appDir: string, matcher: ValidFileMatcher):
306
313
  routeManifest: RouteManifest;
307
314
  }>;
308
315
  declare function computeRootParamNames(routeSegments: readonly string[], layoutTreePositions: readonly number[]): string[];
316
+ declare function patternsStructurallyEquivalent(a: readonly string[], b: readonly string[]): boolean;
309
317
  /**
310
318
  * Find the best route to attach a sibling intercept to, given the directory
311
319
  * that contains the interception marker.
@@ -359,4 +367,4 @@ declare function computeAppRouteStaticSiblings(allRoutes: readonly {
359
367
  patternParts?: readonly string[] | null;
360
368
  }): string[];
361
369
  //#endregion
362
- export { AppRoute, AppRouteGraphRoute, AppRouteSemanticIds, GraphVersion, RootBoundaryId, RouteManifest, RouteManifestInterception, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding, StaticSegmentGraph, buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
370
+ export { AppRoute, AppRouteGraphRoute, AppRouteSemanticIds, GraphVersion, RootBoundaryId, RouteManifest, RouteManifestInterception, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding, StaticSegmentGraph, buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment, patternsStructurallyEquivalent };
@@ -460,7 +460,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
460
460
  if (subPage !== void 0) {
461
461
  const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage, matcher);
462
462
  const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage, matcher);
463
- return {
463
+ return withSlotLoadingEntries({
464
464
  ...slot,
465
465
  pagePath: subPage,
466
466
  configLayoutPaths,
@@ -468,7 +468,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
468
468
  notFoundPath: notFoundBoundary.path,
469
469
  notFoundTreePosition: notFoundBoundary.treePosition,
470
470
  routeSegments: rawSegments
471
- };
471
+ }, subPage, matcher);
472
472
  }
473
473
  return slot;
474
474
  });
@@ -549,7 +549,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
549
549
  const subPage = slotPages.get(slot.key);
550
550
  const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage ?? null, matcher);
551
551
  const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage ?? null, matcher);
552
- return {
552
+ return withSlotLoadingEntries({
553
553
  ...slot,
554
554
  pagePath: subPage || null,
555
555
  configLayoutPaths,
@@ -557,7 +557,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
557
557
  notFoundPath: notFoundBoundary.path,
558
558
  notFoundTreePosition: notFoundBoundary.treePosition,
559
559
  routeSegments: subPage ? rawSegments : null
560
- };
560
+ }, subPage ?? null, matcher);
561
561
  });
562
562
  const newRoute = {
563
563
  ids: createAppRouteSemanticIds({
@@ -580,7 +580,9 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
580
580
  ownerTreePath: childrenOwnerTreePath,
581
581
  state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched"
582
582
  },
583
- loadingPath: parentRoute.loadingPath,
583
+ loadingPath: null,
584
+ loadingPaths: parentRoute.loadingPaths,
585
+ loadingTreePositions: parentRoute.loadingTreePositions,
584
586
  errorPath: parentRoute.errorPath,
585
587
  layoutErrorPaths: parentRoute.layoutErrorPaths,
586
588
  notFoundPath: parentRoute.notFoundPath,
@@ -678,6 +680,36 @@ function findSlotConfigLayoutTreePositions(slotDir, layoutPaths) {
678
680
  return relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0;
679
681
  });
680
682
  }
683
+ function findSlotLoadingEntries(slotDir, pagePath, matcher) {
684
+ const pageDir = pagePath ? path.dirname(pagePath) : slotDir;
685
+ if (pageDir !== slotDir && !pageDir.startsWith(`${slotDir}${path.sep}`)) return [];
686
+ const relativeDir = path.relative(slotDir, pageDir);
687
+ const segments = relativeDir ? relativeDir.split(path.sep).filter(Boolean) : [];
688
+ const loadings = [];
689
+ let currentDir = slotDir;
690
+ const rootLoading = findFile(currentDir, "loading", matcher);
691
+ if (rootLoading) loadings.push({
692
+ path: rootLoading,
693
+ treePosition: 0
694
+ });
695
+ for (let index = 0; index < segments.length; index++) {
696
+ currentDir = path.join(currentDir, segments[index]);
697
+ const loading = findFile(currentDir, "loading", matcher);
698
+ if (loading) loadings.push({
699
+ path: loading,
700
+ treePosition: index + 1
701
+ });
702
+ }
703
+ return loadings;
704
+ }
705
+ function withSlotLoadingEntries(slot, pagePath, matcher) {
706
+ const loadingEntries = findSlotLoadingEntries(slot.ownerDir, pagePath, matcher);
707
+ return {
708
+ ...slot,
709
+ loadingPaths: loadingEntries.map((entry) => entry.path),
710
+ loadingTreePositions: loadingEntries.map((entry) => entry.treePosition)
711
+ };
712
+ }
681
713
  function findSlotNotFoundBoundary(slotDir, pagePath, matcher) {
682
714
  let dir = pagePath ? path.dirname(pagePath) : slotDir;
683
715
  while (dir === slotDir || dir.startsWith(`${slotDir}${path.sep}`)) {
@@ -751,6 +783,9 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
751
783
  const templateTreePositions = computeLayoutTreePositions(appDir, templates);
752
784
  const layoutTreePositions = computeLayoutTreePositions(appDir, layouts);
753
785
  const layoutErrorPaths = discoverLayoutAlignedErrors(segments, appDir, matcher);
786
+ const loadingEntries = discoverSegmentLoadings(segments, appDir, matcher);
787
+ const loadingPaths = loadingEntries.map((entry) => entry.path);
788
+ const loadingTreePositions = loadingEntries.map((entry) => entry.treePosition);
754
789
  const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
755
790
  const errorPaths = errorEntries.map((entry) => entry.path);
756
791
  const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
@@ -785,6 +820,8 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
785
820
  templates,
786
821
  parallelSlots,
787
822
  loadingPath,
823
+ loadingPaths,
824
+ loadingTreePositions,
788
825
  errorPath,
789
826
  layoutErrorPaths,
790
827
  errorPaths,
@@ -933,6 +970,31 @@ function discoverSegmentErrors(segments, appDir, matcher) {
933
970
  return errors;
934
971
  }
935
972
  /**
973
+ * Discover loading.tsx files by segment tree position.
974
+ *
975
+ * Loading conventions belong to loader-tree segments rather than layouts. A
976
+ * segment without layout.tsx can therefore provide the boundary that suspends
977
+ * while its child layout renders.
978
+ */
979
+ function discoverSegmentLoadings(segments, appDir, matcher) {
980
+ const loadings = [];
981
+ const rootLoading = findFile(appDir, "loading", matcher);
982
+ if (rootLoading) loadings.push({
983
+ path: rootLoading,
984
+ treePosition: 0
985
+ });
986
+ let currentDir = appDir;
987
+ for (let index = 0; index < segments.length; index++) {
988
+ currentDir = path.join(currentDir, segments[index]);
989
+ const loading = findFile(currentDir, "loading", matcher);
990
+ if (loading) loadings.push({
991
+ path: loading,
992
+ treePosition: index + 1
993
+ });
994
+ }
995
+ return loadings;
996
+ }
997
+ /**
936
998
  * Discover error.tsx files aligned with the layouts array.
937
999
  *
938
1000
  * Route manifests still model layout-owned boundary facts by layout index.
@@ -1037,7 +1099,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
1037
1099
  slotParamNames = [...ownerUrl?.params ?? [], ...mirror.slotParamNames];
1038
1100
  }
1039
1101
  const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, mirror?.pagePath ?? null, matcher);
1040
- const inheritedSlot = {
1102
+ const inheritedSlot = withSlotLoadingEntries({
1041
1103
  ...slot,
1042
1104
  pagePath: mirror?.pagePath ?? null,
1043
1105
  configLayoutPaths,
@@ -1046,7 +1108,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
1046
1108
  routeSegments: mirror?.segments ?? null,
1047
1109
  slotPatternParts,
1048
1110
  slotParamNames
1049
- };
1111
+ }, mirror?.pagePath ?? null, matcher);
1050
1112
  slotMap.set(slot.key, inheritedSlot);
1051
1113
  }
1052
1114
  }
@@ -1209,6 +1271,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1209
1271
  const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
1210
1272
  const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
1211
1273
  const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
1274
+ const loadingEntries = findSlotLoadingEntries(slotDir, pagePath, matcher);
1212
1275
  const notFoundBoundary = findSlotNotFoundBoundary(slotDir, pagePath, matcher);
1213
1276
  slots.push({
1214
1277
  id: createAppRouteGraphSlotId(slotName, ownerTreePath),
@@ -1216,6 +1279,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1216
1279
  name: slotName,
1217
1280
  ownerDir: slotDir,
1218
1281
  ownerTreePath,
1282
+ ownerTreePosition: ownerSegments.length,
1219
1283
  hasPage: pagePath !== null,
1220
1284
  pagePath,
1221
1285
  defaultPath,
@@ -1223,6 +1287,8 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1223
1287
  configLayoutPaths,
1224
1288
  configLayoutTreePositions: findSlotConfigLayoutTreePositions(slotDir, configLayoutPaths),
1225
1289
  loadingPath: findFile(slotDir, "loading", matcher),
1290
+ loadingPaths: loadingEntries.map((loading) => loading.path),
1291
+ loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
1226
1292
  errorPath: findFile(slotDir, "error", matcher),
1227
1293
  notFoundPath: notFoundBoundary.path,
1228
1294
  notFoundTreePosition: notFoundBoundary.treePosition,
@@ -1374,7 +1440,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
1374
1440
  * Recursively scan a directory tree for page.tsx files that are inside
1375
1441
  * intercepting route directories.
1376
1442
  */
1377
- function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher) {
1443
+ function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher, parentLoadingEntries = []) {
1378
1444
  if (!fs.existsSync(currentDir)) return;
1379
1445
  const entries = fs.readdirSync(currentDir, { withFileTypes: true });
1380
1446
  for (const entry of entries) {
@@ -1384,8 +1450,14 @@ function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, res
1384
1450
  const interceptDir = path.join(currentDir, entry.name);
1385
1451
  if (interceptMatch) {
1386
1452
  const restOfName = entry.name.slice(interceptMatch.prefix.length);
1387
- collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, slotRootDir, results, matcher);
1388
- } else scanForInterceptingPages(interceptDir, slotRootDir, routeDir, appDir, results, matcher);
1453
+ collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, slotRootDir, results, matcher, [], parentLoadingEntries, path.relative(slotRootDir, currentDir).split(path.sep).filter(Boolean).length);
1454
+ } else {
1455
+ const loading = findFile(interceptDir, "loading", matcher);
1456
+ scanForInterceptingPages(interceptDir, slotRootDir, routeDir, appDir, results, matcher, loading ? [...parentLoadingEntries, {
1457
+ path: loading,
1458
+ treePosition: path.relative(slotRootDir, interceptDir).split(path.sep).filter(Boolean).length
1459
+ }] : parentLoadingEntries);
1460
+ }
1389
1461
  }
1390
1462
  }
1391
1463
  /**
@@ -1399,25 +1471,43 @@ function matchInterceptConvention(name) {
1399
1471
  * Collect page.tsx files inside an intercepting route directory tree
1400
1472
  * and compute their target URL patterns.
1401
1473
  */
1402
- function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = []) {
1474
+ function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = [], parentLoadingEntries = [], treePositionOffset = 0) {
1403
1475
  const currentLayoutPath = findFile(currentDir, "layout", matcher);
1404
1476
  const layoutPaths = currentLayoutPath ? [...parentLayoutPaths, currentLayoutPath] : parentLayoutPaths;
1477
+ const currentLoadingPath = findFile(currentDir, "loading", matcher);
1478
+ const relativeCurrentDir = path.relative(interceptRoot, currentDir);
1479
+ const currentTreePosition = treePositionOffset + 1 + (relativeCurrentDir ? relativeCurrentDir.split(path.sep).filter(Boolean).length : 0);
1480
+ const loadingEntries = currentLoadingPath ? [...parentLoadingEntries, {
1481
+ path: currentLoadingPath,
1482
+ treePosition: currentTreePosition
1483
+ }] : parentLoadingEntries;
1405
1484
  const page = findFile(currentDir, "page", matcher);
1406
1485
  if (page) {
1407
1486
  const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
1408
1487
  if (targetPattern) {
1409
1488
  const sourceMatchPattern = computeInterceptSourceMatchPattern(interceptParentDir, appDir);
1410
1489
  const notFoundBoundary = findSlotNotFoundBoundary(slotRootDir ?? interceptRoot, page, matcher);
1411
- const branchSegments = [interceptSegment, ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)];
1412
1490
  const slotParentSegments = slotRootDir ? path.relative(slotRootDir, interceptParentDir).split(path.sep).filter(Boolean) : [];
1491
+ const branchSegments = [
1492
+ ...slotParentSegments,
1493
+ interceptSegment,
1494
+ ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)
1495
+ ];
1413
1496
  results.push({
1414
1497
  branchSegments,
1415
1498
  convention,
1416
1499
  layoutPaths: [...layoutPaths],
1417
1500
  layoutSegments: layoutPaths.map((layoutPath) => {
1418
- return [interceptSegment, ...path.relative(interceptRoot, path.dirname(layoutPath)).split(path.sep).filter(Boolean)];
1501
+ const relativeDir = path.relative(interceptRoot, path.dirname(layoutPath));
1502
+ return [
1503
+ ...slotParentSegments,
1504
+ interceptSegment,
1505
+ ...relativeDir.split(path.sep).filter(Boolean)
1506
+ ];
1419
1507
  }),
1420
- notFoundBranchSegments: [...slotParentSegments, ...branchSegments],
1508
+ loadingPaths: loadingEntries.map((loading) => loading.path),
1509
+ loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
1510
+ notFoundBranchSegments: branchSegments,
1421
1511
  notFoundPath: notFoundBoundary.path,
1422
1512
  notFoundTreePosition: notFoundBoundary.treePosition === null ? null : slotRootDir ? notFoundBoundary.treePosition : notFoundBoundary.treePosition + 1,
1423
1513
  targetPattern: targetPattern.pattern,
@@ -1433,7 +1523,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
1433
1523
  for (const entry of entries) {
1434
1524
  if (!entry.isDirectory()) continue;
1435
1525
  if (entry.name.startsWith("_")) continue;
1436
- collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths);
1526
+ collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths, loadingEntries, treePositionOffset);
1437
1527
  }
1438
1528
  }
1439
1529
  /**
@@ -1661,4 +1751,4 @@ function computeAppRouteStaticSiblings(allRoutes, matchedRoute) {
1661
1751
  return Array.from(siblings);
1662
1752
  }
1663
1753
  //#endregion
1664
- export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
1754
+ export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment, patternsStructurallyEquivalent };
@@ -11,6 +11,8 @@ import { IncomingMessage, ServerResponse } from "node:http";
11
11
  declare function handleApiRoute(runner: ModuleImporter, req: IncomingMessage, res: ServerResponse, url: string, apiRoutes: Route[], nextConfig?: {
12
12
  basePath?: string;
13
13
  i18n?: NextI18nConfig | null;
14
+ trustedRevalidateOrigin?: string;
15
+ allowedRevalidateHeaderKeys?: readonly string[];
14
16
  trailingSlash?: boolean;
15
17
  }): Promise<boolean>;
16
18
  //#endregion
@@ -160,7 +160,7 @@ async function writeEdgeApiResponseBody(res, body) {
160
160
  /**
161
161
  * Enhance a Node.js req/res pair with Next.js API route helpers.
162
162
  */
163
- function enhanceApiObjects(req, res, query, body) {
163
+ function enhanceApiObjects(req, res, query, body, trustedRevalidateOrigin, allowedRevalidateHeaderKeys = [], dev = false) {
164
164
  const apiReq = Object.assign(req, {
165
165
  body,
166
166
  query
@@ -202,7 +202,7 @@ function enhanceApiObjects(req, res, query, body) {
202
202
  return this;
203
203
  },
204
204
  async revalidate(urlPath, opts) {
205
- await performOnDemandRevalidate(req, urlPath, opts);
205
+ await performOnDemandRevalidate(req, urlPath, opts, trustedRevalidateOrigin, allowedRevalidateHeaderKeys, dev);
206
206
  }
207
207
  });
208
208
  attachPagesPreviewApi(apiReq, apiRes);
@@ -248,7 +248,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
248
248
  }
249
249
  const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
250
250
  const bodyParserConfig = resolveBodyParserConfig(apiModule.config);
251
- const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0);
251
+ const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0, nextConfig?.trustedRevalidateOrigin, nextConfig?.allowedRevalidateHeaderKeys, true);
252
252
  await handler(apiReq, apiRes);
253
253
  return true;
254
254
  } catch (e) {
@@ -302,28 +302,29 @@ function createNavigationCommitEffect(options) {
302
302
  commitClientNavigationState(navId);
303
303
  };
304
304
  }
305
- async function renderNavigationPayload(payload, navigationSnapshot, targetHref, navId, historyUpdateMode, params, previousNextUrl, pendingRouterState, payloadOrigin, actionType = "navigate", operationLane = "navigation", traversalIntent = null, scrollIntent = null, restoredBfcacheIds = null, reuseCurrentBfcacheIds = true, visibleCommitMode = "transition", onCommittedState, navigationCommitKind) {
305
+ async function renderNavigationPayload(options) {
306
306
  syncServerActionHttpFallbackHead(null);
307
307
  return browserNavigationController.renderNavigationPayload({
308
- actionType,
308
+ actionType: options.actionType ?? "navigate",
309
309
  createNavigationCommitEffect,
310
- historyUpdateMode,
311
- navigationSnapshot,
312
- nextElements: payload,
313
- operationLane,
314
- payloadOrigin,
315
- params,
316
- pendingRouterState,
317
- previousNextUrl,
318
- scrollIntent,
319
- restoredBfcacheIds,
320
- reuseCurrentBfcacheIds,
321
- targetHistoryIndex: traversalIntent === null ? void 0 : traversalIntent.targetHistoryIndex,
322
- targetHref,
323
- navId,
324
- navigationCommitKind,
325
- visibleCommitMode,
326
- onCommittedState
310
+ historyUpdateMode: options.historyUpdateMode,
311
+ navigationCommitKind: options.navigationCommitKind,
312
+ navigationInitiationState: options.navigationInitiationState,
313
+ navigationSnapshot: options.navigationSnapshot,
314
+ navId: options.navId,
315
+ nextElements: options.payload,
316
+ onCommittedState: options.onCommittedState,
317
+ operationLane: options.operationLane ?? "navigation",
318
+ params: options.params,
319
+ payloadOrigin: options.payloadOrigin,
320
+ pendingRouterState: options.pendingRouterState,
321
+ previousNextUrl: options.previousNextUrl,
322
+ restoredBfcacheIds: options.restoredBfcacheIds ?? null,
323
+ reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds ?? true,
324
+ scrollIntent: options.scrollIntent ?? null,
325
+ targetHistoryIndex: options.traversalIntent?.targetHistoryIndex,
326
+ targetHref: options.targetHref,
327
+ visibleCommitMode: options.visibleCommitMode ?? "transition"
327
328
  });
328
329
  }
329
330
  async function commitSameUrlNavigatePayload(nextElements, actionInitiation, returnValue, revalidation = "none") {
@@ -725,7 +726,21 @@ function registerServerActionCallback() {
725
726
  const hashIdx = target.href.indexOf("#");
726
727
  const actionScrollIntent = beginAppRouterScrollIntent((hashIdx !== -1 ? target.href.slice(hashIdx) : "") || null);
727
728
  if (target.type === "push") saveScrollPosition();
728
- renderNavigationPayload(Promise.resolve(elements), createClientNavigationRenderSnapshot(target.href, actionInitiation.routerState.navigationSnapshot.params), target.href, actionInitiation.navigationId, target.type === "push" ? "push" : "replace", {}, null, null, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, target.type === "push" ? "navigate" : "replace", resolveServerActionOperationLane(revalidation), null, actionScrollIntent).catch(() => {
729
+ renderNavigationPayload({
730
+ actionType: target.type === "push" ? "navigate" : "replace",
731
+ historyUpdateMode: target.type === "push" ? "push" : "replace",
732
+ navigationInitiationState: actionInitiation.routerState,
733
+ navigationSnapshot: createClientNavigationRenderSnapshot(target.href, actionInitiation.routerState.navigationSnapshot.params),
734
+ navId: actionInitiation.navigationId,
735
+ operationLane: resolveServerActionOperationLane(revalidation),
736
+ params: {},
737
+ payload: Promise.resolve(elements),
738
+ payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
739
+ pendingRouterState: null,
740
+ previousNextUrl: null,
741
+ scrollIntent: actionScrollIntent,
742
+ targetHref: target.href
743
+ }).catch(() => {
729
744
  browserNavigationController.performHardNavigation(target.href);
730
745
  });
731
746
  },
@@ -870,6 +885,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
870
885
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
871
886
  if (shouldUsePendingRouterState) pendingRouterState = beginPendingBrowserRouterState();
872
887
  }
888
+ const navigationInitiationState = getBrowserRouterState();
889
+ const mountedSlotsHeader = getMountedSlotIdsHeader(navigationInitiationState.elements);
873
890
  while (true) {
874
891
  const url = new URL(currentHref, window.location.origin);
875
892
  const requestState = getRequestState(navigationKind, url.pathname, currentPrevNextUrl, activeTraversalIntent?.historyState);
@@ -877,12 +894,9 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
877
894
  const requestPreviousNextUrl = requestState.previousNextUrl;
878
895
  if (navigationKind === "refresh") historyController.syncCurrentHistoryStatePreviousNextUrl(requestPreviousNextUrl, getBrowserRouterState().bfcacheIds);
879
896
  setPendingPathname(url.pathname, navId);
880
- const routerStateAtNavStart = getBrowserRouterState();
881
- const elementsAtNavStart = routerStateAtNavStart.elements;
882
- const mountedSlotsHeader = getMountedSlotIdsHeader(elementsAtNavStart);
883
897
  const earlyIntentDecision = navigationKind === "navigate" ? navigationPlanner.classifyEarlyNavigationIntent({
884
898
  basePath: __basePath,
885
- currentHref: clientNavigationSnapshotHref(routerStateAtNavStart.navigationSnapshot),
899
+ currentHref: clientNavigationSnapshotHref(navigationInitiationState.navigationSnapshot),
886
900
  mode: "push",
887
901
  scroll: false,
888
902
  targetHref: url.href
@@ -960,7 +974,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
960
974
  const cachedNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, cachedParams);
961
975
  const cachedPayload = cachedRoute.elements ? Promise.resolve(cachedRoute.elements) : decodeAppElementsPromise(createFromFetch(Promise.resolve(restoreRscResponse(cachedRoute.response))));
962
976
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
963
- if (await renderNavigationPayload(cachedPayload, cachedNavigationSnapshot, currentHref, navId, currentHistoryMode, cachedParams, requestPreviousNextUrl, detachedNavigationCommits ? null : pendingRouterState, cachedRoute.elements ? COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN : VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds, visibleCommitMode) === "no-commit") {
977
+ if (await renderNavigationPayload({
978
+ actionType: toActionType(navigationKind),
979
+ historyUpdateMode: currentHistoryMode,
980
+ navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
981
+ navigationInitiationState,
982
+ navigationSnapshot: cachedNavigationSnapshot,
983
+ navId,
984
+ operationLane: toOperationLane(navigationKind),
985
+ params: cachedParams,
986
+ payload: cachedPayload,
987
+ payloadOrigin: cachedRoute.elements ? COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN : VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN,
988
+ pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
989
+ previousNextUrl: requestPreviousNextUrl,
990
+ restoredBfcacheIds,
991
+ reuseCurrentBfcacheIds,
992
+ scrollIntent,
993
+ targetHref: currentHref,
994
+ traversalIntent: activeTraversalIntent,
995
+ visibleCommitMode
996
+ }) === "no-commit") {
964
997
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
965
998
  deleteVisitedResponse(rscUrl, requestInterceptionContext);
966
999
  continue;
@@ -1016,7 +1049,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1016
1049
  if (optimisticPayload !== null) {
1017
1050
  detachedNavigationCommits = true;
1018
1051
  const optimisticNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, optimisticPayload.params);
1019
- renderNavigationPayload(Promise.resolve(optimisticPayload.elements), optimisticNavigationSnapshot, currentHref, navId, currentHistoryMode, optimisticPayload.params, requestPreviousNextUrl, null, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds, visibleCommitMode, void 0, "detached").catch((error) => {
1052
+ renderNavigationPayload({
1053
+ actionType: toActionType(navigationKind),
1054
+ historyUpdateMode: currentHistoryMode,
1055
+ navigationCommitKind: "detached",
1056
+ navigationInitiationState,
1057
+ navigationSnapshot: optimisticNavigationSnapshot,
1058
+ navId,
1059
+ operationLane: toOperationLane(navigationKind),
1060
+ params: optimisticPayload.params,
1061
+ payload: Promise.resolve(optimisticPayload.elements),
1062
+ payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
1063
+ pendingRouterState: null,
1064
+ previousNextUrl: requestPreviousNextUrl,
1065
+ restoredBfcacheIds,
1066
+ reuseCurrentBfcacheIds,
1067
+ scrollIntent,
1068
+ targetHref: currentHref,
1069
+ traversalIntent: activeTraversalIntent,
1070
+ visibleCommitMode
1071
+ }).catch((error) => {
1020
1072
  if (browserNavigationController.isCurrentNavigation(navId)) console.error("[vinext] Optimistic RSC navigation error:", error);
1021
1073
  });
1022
1074
  }
@@ -1024,7 +1076,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1024
1076
  }
1025
1077
  if (!navResponse) {
1026
1078
  if (navigationKind === "navigate") {
1027
- const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(routerStateAtNavStart);
1079
+ const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(navigationInitiationState);
1028
1080
  if (clientReuseManifestHeader !== null) requestHeaders.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, clientReuseManifestHeader);
1029
1081
  }
1030
1082
  navResponse = await fetch(rscUrl, {
@@ -1086,10 +1138,30 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1086
1138
  const rscPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
1087
1139
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1088
1140
  let committedState = null;
1089
- if (await renderNavigationPayload(rscPayload, navigationSnapshot, currentHref, navId, currentHistoryMode, navParams, requestPreviousNextUrl, detachedNavigationCommits ? null : pendingRouterState, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds, visibleCommitMode, (state) => {
1090
- committedState = state;
1091
- if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
1092
- }, detachedNavigationCommits ? "authoritative" : void 0) !== "committed") return;
1141
+ if (await renderNavigationPayload({
1142
+ actionType: toActionType(navigationKind),
1143
+ historyUpdateMode: currentHistoryMode,
1144
+ navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
1145
+ navigationInitiationState,
1146
+ navigationSnapshot,
1147
+ navId,
1148
+ onCommittedState: (state) => {
1149
+ committedState = state;
1150
+ if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
1151
+ },
1152
+ operationLane: toOperationLane(navigationKind),
1153
+ params: navParams,
1154
+ payload: rscPayload,
1155
+ payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
1156
+ pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
1157
+ previousNextUrl: requestPreviousNextUrl,
1158
+ restoredBfcacheIds,
1159
+ reuseCurrentBfcacheIds,
1160
+ scrollIntent,
1161
+ targetHref: currentHref,
1162
+ traversalIntent: activeTraversalIntent,
1163
+ visibleCommitMode
1164
+ }) !== "committed") return;
1093
1165
  try {
1094
1166
  const renderedElements = await rscPayload;
1095
1167
  if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
@@ -43,6 +43,28 @@ type BrowserNavigationControllerDeps = {
43
43
  getRouteManifest?: () => RouteManifest | null;
44
44
  syncHistoryStatePreviousNextUrl?: (previousNextUrl: string | null, bfcacheIds?: Readonly<Record<string, string>> | null) => void;
45
45
  };
46
+ type BrowserNavigationPayloadOptions = {
47
+ actionType: "navigate" | "replace" | "traverse";
48
+ createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
49
+ historyUpdateMode: HistoryUpdateMode | undefined;
50
+ navigationCommitKind?: "authoritative" | "detached";
51
+ navigationInitiationState: AppRouterState;
52
+ navigationSnapshot: ClientNavigationRenderSnapshot;
53
+ navId: number;
54
+ nextElements: Promise<AppElements>;
55
+ onCommittedState?: (state: AppRouterState) => void;
56
+ operationLane: OperationLane;
57
+ params: Record<string, string | string[]>;
58
+ payloadOrigin: AppNavigationPayloadOrigin;
59
+ pendingRouterState: PendingBrowserRouterState | null;
60
+ previousNextUrl: string | null;
61
+ restoredBfcacheIds?: Readonly<Record<string, string>> | null;
62
+ reuseCurrentBfcacheIds?: boolean;
63
+ scrollIntent?: AppRouterScrollIntent | null;
64
+ targetHistoryIndex?: number | null;
65
+ targetHref: string;
66
+ visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
67
+ };
46
68
  type BrowserNavigationController = {
47
69
  beginNavigation(): number;
48
70
  getActiveNavigationId(): number;
@@ -60,27 +82,7 @@ type BrowserNavigationController = {
60
82
  state: AppRouterState;
61
83
  targetHref: string;
62
84
  }): boolean;
63
- renderNavigationPayload(options: {
64
- actionType: "navigate" | "replace" | "traverse";
65
- createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
66
- historyUpdateMode: HistoryUpdateMode | undefined;
67
- navigationSnapshot: ClientNavigationRenderSnapshot;
68
- nextElements: Promise<AppElements>;
69
- operationLane: OperationLane;
70
- payloadOrigin: AppNavigationPayloadOrigin;
71
- params: Record<string, string | string[]>;
72
- pendingRouterState: PendingBrowserRouterState | null;
73
- previousNextUrl: string | null;
74
- scrollIntent?: AppRouterScrollIntent | null;
75
- restoredBfcacheIds?: Readonly<Record<string, string>> | null;
76
- reuseCurrentBfcacheIds?: boolean;
77
- targetHistoryIndex?: number | null;
78
- targetHref: string;
79
- navId: number;
80
- navigationCommitKind?: "authoritative" | "detached";
81
- visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
82
- onCommittedState?: (state: AppRouterState) => void;
83
- }): Promise<NavigationPayloadOutcome>;
85
+ renderNavigationPayload(options: BrowserNavigationPayloadOptions): Promise<NavigationPayloadOutcome>;
84
86
  commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
85
87
  ok: boolean;
86
88
  data: unknown;
@@ -349,7 +349,7 @@ function createAppBrowserNavigationController(deps = {}) {
349
349
  let snapshotActivated = false;
350
350
  try {
351
351
  const pending = await createPendingNavigationCommit({
352
- currentState: getBrowserRouterState(),
352
+ currentState: options.navigationInitiationState,
353
353
  navigationCommitKind: options.navigationCommitKind,
354
354
  navigationId: options.navId,
355
355
  nextElements: options.nextElements,