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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/build/precompress.js +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +12 -2
  6. package/dist/check.js +4 -0
  7. package/dist/cli-args.d.ts +2 -1
  8. package/dist/cli-args.js +10 -0
  9. package/dist/cli.js +14 -8
  10. package/dist/client/navigation-runtime.d.ts +6 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +4 -1
  13. package/dist/config/dotenv.d.ts +3 -4
  14. package/dist/config/dotenv.js +30 -22
  15. package/dist/config/next-config.d.ts +16 -5
  16. package/dist/config/next-config.js +7 -0
  17. package/dist/entries/app-browser-entry.d.ts +4 -10
  18. package/dist/entries/app-browser-entry.js +20 -4
  19. package/dist/entries/app-rsc-entry.js +21 -3
  20. package/dist/entries/app-rsc-manifest.js +19 -0
  21. package/dist/entries/pages-client-entry.js +0 -1
  22. package/dist/entries/pages-server-entry.js +13 -3
  23. package/dist/index.js +122 -49
  24. package/dist/init-cloudflare.d.ts +3 -1
  25. package/dist/init-cloudflare.js +59 -5
  26. package/dist/plugins/client-reference-dedup.js +7 -5
  27. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  28. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  29. package/dist/plugins/import-meta-url.js +17 -6
  30. package/dist/plugins/require-context.js +20 -15
  31. package/dist/plugins/sass.js +3 -4
  32. package/dist/plugins/transform-cache.d.ts +19 -0
  33. package/dist/plugins/transform-cache.js +36 -0
  34. package/dist/routing/app-route-graph.d.ts +14 -6
  35. package/dist/routing/app-route-graph.js +106 -16
  36. package/dist/server/api-handler.d.ts +2 -0
  37. package/dist/server/api-handler.js +7 -6
  38. package/dist/server/app-bfcache-identity.js +28 -2
  39. package/dist/server/app-browser-entry.js +175 -75
  40. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  41. package/dist/server/app-browser-navigation-controller.js +30 -5
  42. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  43. package/dist/server/app-browser-prefetch-response.js +32 -0
  44. package/dist/server/app-browser-state.d.ts +8 -3
  45. package/dist/server/app-browser-state.js +9 -3
  46. package/dist/server/app-page-cache-finalizer.js +1 -1
  47. package/dist/server/app-page-cache.d.ts +1 -1
  48. package/dist/server/app-page-cache.js +16 -2
  49. package/dist/server/app-page-dispatch.d.ts +9 -1
  50. package/dist/server/app-page-dispatch.js +37 -8
  51. package/dist/server/app-page-element-builder.d.ts +4 -1
  52. package/dist/server/app-page-element-builder.js +34 -7
  53. package/dist/server/app-page-head.d.ts +3 -3
  54. package/dist/server/app-page-head.js +58 -13
  55. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  56. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  57. package/dist/server/app-page-probe.d.ts +4 -0
  58. package/dist/server/app-page-probe.js +6 -2
  59. package/dist/server/app-page-render.d.ts +1 -1
  60. package/dist/server/app-page-render.js +13 -3
  61. package/dist/server/app-page-request.d.ts +3 -1
  62. package/dist/server/app-page-request.js +4 -13
  63. package/dist/server/app-page-response.d.ts +2 -0
  64. package/dist/server/app-page-response.js +7 -2
  65. package/dist/server/app-page-route-wiring.d.ts +10 -2
  66. package/dist/server/app-page-route-wiring.js +269 -59
  67. package/dist/server/app-pages-bridge.d.ts +2 -1
  68. package/dist/server/app-pages-bridge.js +3 -1
  69. package/dist/server/app-route-handler-cache.js +4 -0
  70. package/dist/server/app-route-handler-dispatch.js +9 -3
  71. package/dist/server/app-route-handler-response.js +1 -1
  72. package/dist/server/app-route-module-loader.d.ts +6 -0
  73. package/dist/server/app-route-module-loader.js +3 -0
  74. package/dist/server/app-router-entry.js +6 -3
  75. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  76. package/dist/server/app-rsc-cache-busting.js +6 -0
  77. package/dist/server/app-rsc-errors.js +10 -0
  78. package/dist/server/app-rsc-handler.d.ts +2 -0
  79. package/dist/server/app-rsc-handler.js +30 -13
  80. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  81. package/dist/server/app-rsc-render-mode.js +4 -1
  82. package/dist/server/app-rsc-request-normalization.js +65 -4
  83. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  84. package/dist/server/app-rsc-route-matching.js +6 -0
  85. package/dist/server/app-server-action-execution.d.ts +1 -1
  86. package/dist/server/app-ssr-entry.js +4 -4
  87. package/dist/server/app-visited-response-cache.d.ts +6 -1
  88. package/dist/server/app-visited-response-cache.js +49 -1
  89. package/dist/server/cache-control.d.ts +2 -2
  90. package/dist/server/cache-control.js +2 -1
  91. package/dist/server/dev-server.d.ts +3 -1
  92. package/dist/server/dev-server.js +204 -348
  93. package/dist/server/edge-api-runtime.d.ts +10 -1
  94. package/dist/server/edge-api-runtime.js +29 -1
  95. package/dist/server/headers.d.ts +4 -2
  96. package/dist/server/headers.js +6 -3
  97. package/dist/server/isr-cache.d.ts +10 -33
  98. package/dist/server/isr-cache.js +33 -43
  99. package/dist/server/isr-decision.d.ts +2 -2
  100. package/dist/server/pages-api-route.d.ts +4 -0
  101. package/dist/server/pages-api-route.js +6 -3
  102. package/dist/server/pages-asset-tags.d.ts +1 -0
  103. package/dist/server/pages-asset-tags.js +6 -4
  104. package/dist/server/pages-dev-hydration.js +0 -1
  105. package/dist/server/pages-document-asset-props.d.ts +26 -0
  106. package/dist/server/pages-document-asset-props.js +69 -0
  107. package/dist/server/pages-get-initial-props.d.ts +1 -1
  108. package/dist/server/pages-get-initial-props.js +1 -4
  109. package/dist/server/pages-i18n.js +64 -16
  110. package/dist/server/pages-node-compat.d.ts +2 -0
  111. package/dist/server/pages-node-compat.js +10 -6
  112. package/dist/server/pages-page-data.d.ts +61 -9
  113. package/dist/server/pages-page-data.js +334 -74
  114. package/dist/server/pages-page-handler.d.ts +6 -1
  115. package/dist/server/pages-page-handler.js +119 -20
  116. package/dist/server/pages-page-response.d.ts +7 -3
  117. package/dist/server/pages-page-response.js +41 -33
  118. package/dist/server/pages-request-pipeline.d.ts +7 -0
  119. package/dist/server/pages-request-pipeline.js +5 -1
  120. package/dist/server/pages-revalidate.d.ts +1 -1
  121. package/dist/server/pages-revalidate.js +54 -8
  122. package/dist/server/pages-router-entry.d.ts +1 -0
  123. package/dist/server/pages-router-entry.js +10 -4
  124. package/dist/server/prerender-manifest.d.ts +1 -0
  125. package/dist/server/prod-server.d.ts +2 -1
  126. package/dist/server/prod-server.js +38 -13
  127. package/dist/server/revalidation-host.d.ts +11 -0
  128. package/dist/server/revalidation-host.js +19 -0
  129. package/dist/server/seed-cache.js +1 -1
  130. package/dist/server/worker-revalidation-context.d.ts +13 -0
  131. package/dist/server/worker-revalidation-context.js +30 -0
  132. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  133. package/dist/shims/app-router-scroll-state.js +7 -2
  134. package/dist/shims/cache-handler.d.ts +1 -1
  135. package/dist/shims/cache-handler.js +10 -7
  136. package/dist/shims/cache-runtime.d.ts +6 -0
  137. package/dist/shims/cache-runtime.js +4 -1
  138. package/dist/shims/cache.js +1 -1
  139. package/dist/shims/document.js +15 -2
  140. package/dist/shims/error.js +1 -1
  141. package/dist/shims/fetch-cache.js +1 -1
  142. package/dist/shims/font-google-base.js +17 -8
  143. package/dist/shims/font-local.js +41 -10
  144. package/dist/shims/hash-scroll.d.ts +1 -1
  145. package/dist/shims/hash-scroll.js +2 -1
  146. package/dist/shims/headers.d.ts +10 -13
  147. package/dist/shims/headers.js +85 -36
  148. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  149. package/dist/shims/internal/cookie-serialize.js +9 -8
  150. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  151. package/dist/shims/internal/interpolate-as.js +27 -2
  152. package/dist/shims/link.d.ts +2 -0
  153. package/dist/shims/link.js +77 -24
  154. package/dist/shims/metadata.d.ts +18 -34
  155. package/dist/shims/metadata.js +98 -56
  156. package/dist/shims/navigation.d.ts +18 -2
  157. package/dist/shims/navigation.js +70 -21
  158. package/dist/shims/request-context.d.ts +6 -2
  159. package/dist/shims/router.js +18 -7
  160. package/dist/shims/script.js +1 -1
  161. package/dist/shims/server.d.ts +7 -6
  162. package/dist/shims/server.js +156 -57
  163. package/dist/shims/slot.d.ts +2 -1
  164. package/dist/shims/slot.js +6 -2
  165. package/dist/shims/streamed-icons.d.ts +9 -0
  166. package/dist/shims/streamed-icons.js +33 -0
  167. package/dist/shims/unified-request-context.d.ts +21 -2
  168. package/dist/shims/unified-request-context.js +82 -1
  169. package/dist/utils/cache-control-metadata.d.ts +2 -1
  170. package/dist/utils/cache-control-metadata.js +5 -1
  171. package/dist/utils/html-limited-bots.js +1 -1
  172. package/dist/utils/protocol-headers.d.ts +7 -1
  173. package/dist/utils/protocol-headers.js +7 -1
  174. package/package.json +1 -1
@@ -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
@@ -9,7 +9,7 @@ import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-medi
9
9
  import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
10
10
  import { performOnDemandRevalidate } from "./pages-revalidate.js";
11
11
  import { attachPagesPreviewApi, attachPagesRequestCookies } from "./pages-node-compat.js";
12
- import { isEdgeApiRuntime } from "./edge-api-runtime.js";
12
+ import { finalizeEdgeApiResponse, isEdgeApiRuntime } from "./edge-api-runtime.js";
13
13
  import { decode } from "node:querystring";
14
14
  import { Buffer } from "node:buffer";
15
15
  //#region src/server/api-handler.ts
@@ -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);
@@ -227,8 +227,9 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
227
227
  i18n: nextConfig.i18n ?? void 0,
228
228
  trailingSlash: nextConfig.trailingSlash
229
229
  } } : void 0);
230
- const response = await apiModule.default(nextRequest);
231
- if (!(response instanceof Response)) throw new Error("Edge API route did not return a Response");
230
+ const handlerResponse = await apiModule.default(nextRequest);
231
+ if (!(handlerResponse instanceof Response)) throw new Error("Edge API route did not return a Response");
232
+ const response = finalizeEdgeApiResponse(handlerResponse, "node");
232
233
  res.statusCode = response.status;
233
234
  res.statusMessage = response.statusText;
234
235
  const setCookieHeaders = response.headers.getSetCookie();
@@ -248,7 +249,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
248
249
  }
249
250
  const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
250
251
  const bodyParserConfig = resolveBodyParserConfig(apiModule.config);
251
- const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0);
252
+ const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0, nextConfig?.trustedRevalidateOrigin, nextConfig?.allowedRevalidateHeaderKeys, true);
252
253
  await handler(apiReq, apiRes);
253
254
  return true;
254
255
  } catch (e) {
@@ -51,19 +51,45 @@ function createActiveSlotIdentity(id, parsed) {
51
51
  if (interception?.slotId !== id) return null;
52
52
  return `${id}@${interception.targetRouteId}`;
53
53
  }
54
+ function getDirectMatchedRoutePathname(metadata) {
55
+ if (metadata?.metadata.interception != null || metadata?.metadata.sourcePage == null) return null;
56
+ const route = AppElementsWire.parseElementKey(metadata.metadata.routeId);
57
+ return route?.kind === "route" ? route.path : null;
58
+ }
59
+ function getPageMatchedUrl(parsed, metadata) {
60
+ const interception = metadata?.metadata.interception;
61
+ if (interception === null || interception === void 0) {
62
+ const matchedRoutePathname = getDirectMatchedRoutePathname(metadata);
63
+ return matchedRoutePathname === parsed.path ? matchedRoutePathname : null;
64
+ }
65
+ for (const [routeId, matchedUrl] of [[interception.sourceRouteId, interception.sourceMatchedUrl], [interception.targetRouteId, interception.targetMatchedUrl]]) {
66
+ const route = AppElementsWire.parseElementKey(routeId);
67
+ if (route?.kind === "route" && route.path === parsed.path) return matchedUrl;
68
+ }
69
+ return null;
70
+ }
71
+ function getTreePathname(treePath, metadata) {
72
+ const interception = metadata?.metadata.interception;
73
+ if (interception === null || interception === void 0) return getDirectMatchedRoutePathname(metadata);
74
+ const slot = AppElementsWire.parseElementKey(interception.slotId);
75
+ if (slot?.kind !== "slot") return interception.sourceMatchedUrl;
76
+ const treeSegments = splitPathSegments(treePath);
77
+ const ownerSegments = splitPathSegments(slot.treePath);
78
+ return treeSegments.length > ownerSegments.length && ownerSegments.every((segment, index) => treeSegments[index] === segment) && treeSegments[ownerSegments.length] === `@${slot.name}` ? interception.targetMatchedUrl : interception.sourceMatchedUrl;
79
+ }
54
80
  /**
55
81
  * Derive BFCache identity from AppElements wire keys. Keep wire-key parsing
56
82
  * contained here until vinext has a route-manifest authority equivalent to
57
83
  * Next.js CacheNode or segment-cache state.
58
84
  */
59
85
  function createBfcacheSegmentIdentity(id, parsed, options) {
60
- if (parsed.kind === "page") return `${id}@${options.pathname}`;
86
+ if (parsed.kind === "page") return `${id}@${getPageMatchedUrl(parsed, options.metadata) ?? options.pathname}`;
61
87
  if (parsed.kind === "slot") {
62
88
  const activeSlotIdentity = createActiveSlotIdentity(id, options.metadata);
63
89
  if (activeSlotIdentity !== null) return activeSlotIdentity;
64
90
  return `${id}@${getTreePathIdentityPrefix(options.pathname, parsed.treePath)}`;
65
91
  }
66
- if (parsed.kind === "layout" || parsed.kind === "template") return `${id}@${getTreePathIdentityPrefix(options.pathname, parsed.treePath)}`;
92
+ if (parsed.kind === "layout" || parsed.kind === "template") return `${id}@${getTreePathIdentityPrefix(getTreePathname(parsed.treePath, options.metadata) ?? options.pathname, parsed.treePath)}`;
67
93
  return null;
68
94
  }
69
95
  function collectBfcacheSegmentIdCandidates(elements, metadata = readAppElementsMetadata(elements)) {