vinext 0.1.0 → 0.1.2

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 (205) hide show
  1. package/README.md +2 -5
  2. package/dist/build/assets-ignore.d.ts +32 -0
  3. package/dist/build/assets-ignore.js +48 -0
  4. package/dist/build/client-build-config.d.ts +33 -1
  5. package/dist/build/client-build-config.js +66 -1
  6. package/dist/check.js +4 -3
  7. package/dist/cli.js +2 -0
  8. package/dist/client/navigation-runtime.d.ts +11 -2
  9. package/dist/client/navigation-runtime.js +1 -1
  10. package/dist/client/vinext-next-data.d.ts +2 -1
  11. package/dist/client/window-next.d.ts +6 -4
  12. package/dist/config/config-matchers.d.ts +31 -5
  13. package/dist/config/config-matchers.js +50 -3
  14. package/dist/config/next-config.d.ts +29 -3
  15. package/dist/config/next-config.js +32 -2
  16. package/dist/deploy.js +47 -304
  17. package/dist/entries/app-rsc-entry.d.ts +8 -2
  18. package/dist/entries/app-rsc-entry.js +61 -5
  19. package/dist/entries/app-rsc-manifest.js +20 -2
  20. package/dist/entries/pages-client-entry.js +1 -1
  21. package/dist/entries/pages-server-entry.js +16 -7
  22. package/dist/index.d.ts +0 -2
  23. package/dist/index.js +233 -280
  24. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  25. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  26. package/dist/plugins/og-assets.js +2 -2
  27. package/dist/plugins/optimize-imports.d.ts +8 -4
  28. package/dist/plugins/optimize-imports.js +16 -12
  29. package/dist/plugins/postcss.js +18 -14
  30. package/dist/plugins/require-context.d.ts +6 -0
  31. package/dist/plugins/require-context.js +184 -0
  32. package/dist/plugins/sass.d.ts +53 -24
  33. package/dist/plugins/sass.js +249 -1
  34. package/dist/plugins/wasm-module-import.d.ts +15 -0
  35. package/dist/plugins/wasm-module-import.js +50 -0
  36. package/dist/routing/app-route-graph.d.ts +35 -2
  37. package/dist/routing/app-route-graph.js +179 -8
  38. package/dist/routing/file-matcher.js +1 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +16 -1
  41. package/dist/server/api-handler.js +4 -0
  42. package/dist/server/app-browser-entry.js +155 -215
  43. package/dist/server/app-browser-error.d.ts +4 -1
  44. package/dist/server/app-browser-error.js +7 -1
  45. package/dist/server/app-browser-history-controller.d.ts +104 -0
  46. package/dist/server/app-browser-history-controller.js +210 -0
  47. package/dist/server/app-browser-interception-context.d.ts +2 -1
  48. package/dist/server/app-browser-interception-context.js +15 -2
  49. package/dist/server/app-browser-navigation-controller.d.ts +13 -2
  50. package/dist/server/app-browser-navigation-controller.js +83 -4
  51. package/dist/server/app-browser-popstate.d.ts +12 -3
  52. package/dist/server/app-browser-popstate.js +19 -4
  53. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  54. package/dist/server/app-browser-rsc-redirect.js +30 -8
  55. package/dist/server/app-browser-state.d.ts +3 -0
  56. package/dist/server/app-browser-state.js +10 -10
  57. package/dist/server/app-browser-visible-commit.js +10 -8
  58. package/dist/server/app-fallback-renderer.d.ts +2 -1
  59. package/dist/server/app-fallback-renderer.js +3 -1
  60. package/dist/server/app-history-state.d.ts +45 -1
  61. package/dist/server/app-history-state.js +109 -1
  62. package/dist/server/app-middleware.js +1 -0
  63. package/dist/server/app-optimistic-routing.js +22 -1
  64. package/dist/server/app-page-boundary-render.d.ts +2 -1
  65. package/dist/server/app-page-boundary-render.js +45 -21
  66. package/dist/server/app-page-cache.js +9 -7
  67. package/dist/server/app-page-dispatch.d.ts +14 -0
  68. package/dist/server/app-page-dispatch.js +21 -6
  69. package/dist/server/app-page-element-builder.d.ts +23 -2
  70. package/dist/server/app-page-element-builder.js +58 -17
  71. package/dist/server/app-page-execution.d.ts +1 -1
  72. package/dist/server/app-page-execution.js +32 -17
  73. package/dist/server/app-page-render.d.ts +7 -1
  74. package/dist/server/app-page-render.js +11 -16
  75. package/dist/server/app-page-request.d.ts +9 -6
  76. package/dist/server/app-page-request.js +14 -10
  77. package/dist/server/app-page-response.d.ts +2 -2
  78. package/dist/server/app-page-response.js +2 -2
  79. package/dist/server/app-page-route-wiring.d.ts +3 -1
  80. package/dist/server/app-page-route-wiring.js +10 -8
  81. package/dist/server/app-page-stream.d.ts +37 -7
  82. package/dist/server/app-page-stream.js +36 -6
  83. package/dist/server/app-pages-bridge.d.ts +16 -0
  84. package/dist/server/app-pages-bridge.js +23 -3
  85. package/dist/server/app-route-handler-cache.d.ts +1 -0
  86. package/dist/server/app-route-handler-cache.js +1 -0
  87. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  88. package/dist/server/app-route-handler-dispatch.js +2 -0
  89. package/dist/server/app-route-handler-execution.d.ts +1 -0
  90. package/dist/server/app-route-handler-execution.js +1 -0
  91. package/dist/server/app-route-handler-response.js +11 -10
  92. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  93. package/dist/server/app-route-handler-runtime.js +15 -3
  94. package/dist/server/app-rsc-handler.d.ts +1 -0
  95. package/dist/server/app-rsc-handler.js +5 -4
  96. package/dist/server/app-rsc-response-finalizer.js +1 -1
  97. package/dist/server/app-rsc-route-matching.d.ts +20 -1
  98. package/dist/server/app-rsc-route-matching.js +29 -4
  99. package/dist/server/app-server-action-execution.d.ts +22 -1
  100. package/dist/server/app-server-action-execution.js +73 -12
  101. package/dist/server/app-ssr-entry.d.ts +6 -0
  102. package/dist/server/app-ssr-entry.js +19 -3
  103. package/dist/server/app-ssr-stream.js +9 -1
  104. package/dist/server/dev-lockfile.js +2 -1
  105. package/dist/server/dev-server.d.ts +1 -1
  106. package/dist/server/dev-server.js +97 -43
  107. package/dist/server/headers.d.ts +8 -1
  108. package/dist/server/headers.js +8 -1
  109. package/dist/server/instrumentation-runtime.d.ts +6 -0
  110. package/dist/server/instrumentation-runtime.js +8 -0
  111. package/dist/server/isr-cache.d.ts +37 -1
  112. package/dist/server/isr-cache.js +85 -1
  113. package/dist/server/isr-decision.d.ts +79 -0
  114. package/dist/server/isr-decision.js +70 -0
  115. package/dist/server/metadata-route-response.js +5 -3
  116. package/dist/server/middleware-runtime.d.ts +13 -0
  117. package/dist/server/middleware-runtime.js +11 -7
  118. package/dist/server/middleware.js +1 -0
  119. package/dist/server/navigation-planner.d.ts +62 -1
  120. package/dist/server/navigation-planner.js +193 -3
  121. package/dist/server/navigation-trace.d.ts +12 -2
  122. package/dist/server/navigation-trace.js +11 -1
  123. package/dist/server/normalize-path.d.ts +0 -8
  124. package/dist/server/normalize-path.js +3 -1
  125. package/dist/server/otel-tracer-extension.d.ts +45 -0
  126. package/dist/server/otel-tracer-extension.js +89 -0
  127. package/dist/server/pages-api-route.d.ts +14 -3
  128. package/dist/server/pages-api-route.js +6 -1
  129. package/dist/server/pages-asset-tags.d.ts +15 -4
  130. package/dist/server/pages-asset-tags.js +18 -12
  131. package/dist/server/pages-data-route.js +5 -1
  132. package/dist/server/pages-node-compat.d.ts +5 -11
  133. package/dist/server/pages-node-compat.js +175 -118
  134. package/dist/server/pages-page-data.d.ts +38 -7
  135. package/dist/server/pages-page-data.js +64 -18
  136. package/dist/server/pages-page-handler.d.ts +10 -2
  137. package/dist/server/pages-page-handler.js +49 -20
  138. package/dist/server/pages-page-response.d.ts +55 -2
  139. package/dist/server/pages-page-response.js +74 -6
  140. package/dist/server/pages-readiness.d.ts +36 -0
  141. package/dist/server/pages-readiness.js +21 -0
  142. package/dist/server/pages-request-pipeline.d.ts +113 -0
  143. package/dist/server/pages-request-pipeline.js +230 -0
  144. package/dist/server/pages-revalidate.d.ts +15 -0
  145. package/dist/server/pages-revalidate.js +19 -0
  146. package/dist/server/prod-server.d.ts +45 -3
  147. package/dist/server/prod-server.js +182 -234
  148. package/dist/server/socket-error-backstop.d.ts +19 -1
  149. package/dist/server/socket-error-backstop.js +77 -4
  150. package/dist/shims/app-router-scroll.js +22 -4
  151. package/dist/shims/cache-runtime.js +39 -2
  152. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  153. package/dist/shims/dynamic-preload-chunks.js +77 -0
  154. package/dist/shims/dynamic.d.ts +4 -0
  155. package/dist/shims/dynamic.js +4 -2
  156. package/dist/shims/error-boundary.d.ts +17 -7
  157. package/dist/shims/error-boundary.js +8 -1
  158. package/dist/shims/error.js +37 -11
  159. package/dist/shims/fetch-cache.d.ts +22 -1
  160. package/dist/shims/fetch-cache.js +28 -1
  161. package/dist/shims/hash-scroll.d.ts +1 -0
  162. package/dist/shims/hash-scroll.js +3 -1
  163. package/dist/shims/head.js +6 -1
  164. package/dist/shims/headers.d.ts +16 -2
  165. package/dist/shims/headers.js +37 -1
  166. package/dist/shims/image-config.js +7 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  168. package/dist/shims/internal/app-route-detection.js +10 -6
  169. package/dist/shims/internal/app-router-context.d.ts +5 -0
  170. package/dist/shims/internal/link-status-registry.d.ts +43 -0
  171. package/dist/shims/internal/link-status-registry.js +42 -0
  172. package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
  173. package/dist/shims/internal/route-pattern-for-warning.js +40 -0
  174. package/dist/shims/internal/utils.d.ts +1 -0
  175. package/dist/shims/link.js +20 -6
  176. package/dist/shims/metadata.d.ts +6 -2
  177. package/dist/shims/metadata.js +32 -14
  178. package/dist/shims/navigation.d.ts +9 -18
  179. package/dist/shims/navigation.js +96 -23
  180. package/dist/shims/router-state.d.ts +1 -0
  181. package/dist/shims/router-state.js +2 -0
  182. package/dist/shims/router.d.ts +6 -3
  183. package/dist/shims/router.js +156 -22
  184. package/dist/shims/script-nonce-context.d.ts +1 -1
  185. package/dist/shims/script-nonce-context.js +11 -3
  186. package/dist/shims/server.d.ts +17 -1
  187. package/dist/shims/server.js +31 -6
  188. package/dist/shims/slot.js +1 -1
  189. package/dist/shims/unified-request-context.js +1 -0
  190. package/dist/typegen.js +1 -0
  191. package/dist/utils/client-build-manifest.d.ts +8 -1
  192. package/dist/utils/client-build-manifest.js +41 -6
  193. package/dist/utils/client-entry-manifest.d.ts +11 -0
  194. package/dist/utils/client-entry-manifest.js +29 -0
  195. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  196. package/dist/utils/client-runtime-metadata.js +63 -0
  197. package/dist/utils/hash.d.ts +17 -1
  198. package/dist/utils/hash.js +36 -1
  199. package/dist/utils/lazy-chunks.d.ts +27 -1
  200. package/dist/utils/lazy-chunks.js +65 -1
  201. package/dist/utils/manifest-paths.d.ts +20 -2
  202. package/dist/utils/manifest-paths.js +38 -3
  203. package/dist/utils/path.d.ts +2 -1
  204. package/dist/utils/path.js +5 -1
  205. package/package.json +6 -2
@@ -1,7 +1,7 @@
1
1
  //#region src/server/app-browser-rsc-redirect.d.ts
2
- declare const MAX_RSC_REDIRECT_DEPTH = 10;
3
2
  type RscRedirectHistoryUpdateMode = "push" | "replace" | undefined;
4
3
  type RscRedirectLifecycleDecision = {
4
+ href: string;
5
5
  kind: "no-redirect";
6
6
  } | {
7
7
  href: string;
@@ -24,5 +24,14 @@ declare function resolveRscRedirectLifecycleHop(options: {
24
24
  requestPreviousNextUrl: string | null;
25
25
  responseUrl: string;
26
26
  }): RscRedirectLifecycleDecision;
27
+ declare function resolveStreamedRscRedirectLifecycleHop(options: {
28
+ currentHref: string;
29
+ historyUpdateMode: Exclude<RscRedirectHistoryUpdateMode, undefined>;
30
+ maxRedirectDepth?: number;
31
+ origin: string;
32
+ redirectDepth: number;
33
+ requestPreviousNextUrl: string | null;
34
+ streamedRedirectTarget: string;
35
+ }): RscRedirectLifecycleDecision;
27
36
  //#endregion
28
- export { MAX_RSC_REDIRECT_DEPTH, resolveRscRedirectLifecycleHop };
37
+ export { resolveRscRedirectLifecycleHop, resolveStreamedRscRedirectLifecycleHop };
@@ -6,17 +6,23 @@ function toVisibleAppHref(href, origin) {
6
6
  stripRscCacheBustingSearchParam(url);
7
7
  return `${stripRscSuffix(url.pathname)}${url.search}${url.hash}`;
8
8
  }
9
- function resolveRscRedirectLifecycleHop(options) {
10
- const responseUrl = new URL(options.responseUrl, options.origin);
11
- if (responseUrl.origin !== options.origin) return {
12
- href: responseUrl.href,
9
+ function toStreamedRedirectVisibleAppHref(href, origin) {
10
+ const url = new URL(href, origin);
11
+ return `${url.pathname}${url.search}${url.hash}`;
12
+ }
13
+ function resolveRedirectLifecycleHopFromTarget(options) {
14
+ if (options.targetUrl.origin !== options.origin) return {
15
+ href: options.targetUrl.href,
13
16
  kind: "terminal-hard-navigation",
14
17
  reason: "externalRedirect",
15
18
  redirectDepth: options.redirectDepth
16
19
  };
17
- const redirectedHref = resolveHardNavigationTargetFromRscResponse(responseUrl.href, options.currentHref, options.origin);
18
- if (redirectedHref === toVisibleAppHref(options.currentHref, options.origin)) return { kind: "no-redirect" };
19
- const maxRedirectDepth = options.maxRedirectDepth ?? 10;
20
+ const redirectedHref = options.redirectedHref;
21
+ if (redirectedHref === toVisibleAppHref(options.currentHref, options.origin)) return {
22
+ href: redirectedHref,
23
+ kind: "no-redirect"
24
+ };
25
+ const maxRedirectDepth = options.maxRedirectDepth ?? MAX_RSC_REDIRECT_DEPTH;
20
26
  if (options.redirectDepth >= maxRedirectDepth) return {
21
27
  href: redirectedHref,
22
28
  kind: "terminal-hard-navigation",
@@ -31,5 +37,21 @@ function resolveRscRedirectLifecycleHop(options) {
31
37
  redirectDepth: options.redirectDepth + 1
32
38
  };
33
39
  }
40
+ function resolveRscRedirectLifecycleHop(options) {
41
+ const responseUrl = new URL(options.responseUrl, options.origin);
42
+ return resolveRedirectLifecycleHopFromTarget({
43
+ ...options,
44
+ redirectedHref: resolveHardNavigationTargetFromRscResponse(responseUrl.href, options.currentHref, options.origin),
45
+ targetUrl: responseUrl
46
+ });
47
+ }
48
+ function resolveStreamedRscRedirectLifecycleHop(options) {
49
+ const streamedRedirectUrl = new URL(options.streamedRedirectTarget, options.origin);
50
+ return resolveRedirectLifecycleHopFromTarget({
51
+ ...options,
52
+ redirectedHref: toStreamedRedirectVisibleAppHref(options.streamedRedirectTarget, options.origin),
53
+ targetUrl: streamedRedirectUrl
54
+ });
55
+ }
34
56
  //#endregion
35
- export { MAX_RSC_REDIRECT_DEPTH, resolveRscRedirectLifecycleHop };
57
+ export { resolveRscRedirectLifecycleHop, resolveStreamedRscRedirectLifecycleHop };
@@ -49,6 +49,7 @@ type AppRouterAction = {
49
49
  previousNextUrl: string | null;
50
50
  renderId: number;
51
51
  rootLayoutTreePath: string | null;
52
+ reuseCurrentBfcacheIds: boolean;
52
53
  routeId: string;
53
54
  skippedLayoutIds: readonly string[];
54
55
  slotBindings: readonly AppElementsSlotBinding[];
@@ -102,6 +103,7 @@ declare function createNextBfcacheIdMap(options: {
102
103
  elements: AppElements;
103
104
  nextPathname: string;
104
105
  restored?: BfcacheIdMap | null;
106
+ reuseCurrent?: boolean;
105
107
  }): BfcacheIdMap;
106
108
  declare function preserveBfcacheIdsForMergedElements(options: {
107
109
  elements: AppElements;
@@ -150,6 +152,7 @@ declare function createPendingNavigationCommit(options: {
150
152
  previousNextUrl?: string | null;
151
153
  renderId: number;
152
154
  restoredBfcacheIds?: BfcacheIdMap | null;
155
+ reuseCurrentBfcacheIds?: boolean;
153
156
  type: "navigate" | "replace" | "traverse";
154
157
  }): Promise<PendingNavigationCommit>;
155
158
  //#endregion
@@ -7,9 +7,10 @@ import { getMountedSlotIds, getMountedSlotIdsHeader } from "./app-elements.js";
7
7
  import "./app-bfcache-id.js";
8
8
  import { createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isBfcacheSegmentId, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
9
9
  import { createRscRequestHeaders } from "./app-rsc-cache-busting.js";
10
- import { createCacheEntryReuseProof } from "./cache-proof.js";
11
10
  import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
12
11
  import { navigationPlanner, resolveDefaultOrUnmatchedSlotPersistenceForLayouts } from "./navigation-planner.js";
12
+ import { createSnapshotPathAndSearch } from "../shims/navigation.js";
13
+ import { createCacheEntryReuseProof } from "./cache-proof.js";
13
14
  //#region src/server/app-browser-state.ts
14
15
  const FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "fresh" };
15
16
  const VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "visited-cache" };
@@ -99,7 +100,8 @@ function createBfcacheSegmentStateKeyMap(options) {
99
100
  return stateKeys;
100
101
  }
101
102
  function createNextBfcacheIdMap(options) {
102
- for (const value of Object.values(options.current)) rememberBfcacheId(value);
103
+ const current = options.reuseCurrent === false ? {} : options.current;
104
+ for (const value of Object.values(current)) rememberBfcacheId(value);
103
105
  for (const value of Object.values(options.restored ?? {})) rememberBfcacheId(value);
104
106
  const currentMetadata = readAppElementsMetadata(options.currentElements);
105
107
  const nextMetadata = readAppElementsMetadata(options.elements);
@@ -113,7 +115,7 @@ function createNextBfcacheIdMap(options) {
113
115
  }) === createBfcacheSegmentIdentity(id, {
114
116
  metadata: nextMetadata,
115
117
  pathname: nextPathname
116
- }) ? options.current[id] : void 0;
118
+ }) ? current[id] : void 0;
117
119
  const value = options.restored?.[id] ?? currentValue ?? mintBfcacheId();
118
120
  ids[id] = value;
119
121
  rememberBfcacheId(value);
@@ -221,10 +223,6 @@ function createPendingNavigationTraceFields(options) {
221
223
  ...options.targetHref !== void 0 ? { targetHref: options.targetHref } : {}
222
224
  };
223
225
  }
224
- function createNavigationSnapshotUrl(snapshot) {
225
- const query = snapshot.searchParams.toString();
226
- return query === "" ? snapshot.pathname : `${snapshot.pathname}?${query}`;
227
- }
228
226
  function createMountedParallelSlotSnapshots(elements) {
229
227
  const snapshots = [];
230
228
  for (const slotId of getMountedSlotIds(elements)) {
@@ -238,7 +236,7 @@ function createMountedParallelSlotSnapshots(elements) {
238
236
  return snapshots;
239
237
  }
240
238
  function createVisibleRouteSnapshot(state) {
241
- const displayUrl = createNavigationSnapshotUrl(state.navigationSnapshot);
239
+ const displayUrl = createSnapshotPathAndSearch(state.navigationSnapshot);
242
240
  const matchedUrl = normalizeNavigationSnapshotMatchedUrl(state.navigationSnapshot.pathname);
243
241
  return {
244
242
  displayUrl,
@@ -256,7 +254,7 @@ function createVisibleRouteSnapshot(state) {
256
254
  };
257
255
  }
258
256
  function createPendingRouteSnapshot(pending) {
259
- const displayUrl = createNavigationSnapshotUrl(pending.action.navigationSnapshot);
257
+ const displayUrl = createSnapshotPathAndSearch(pending.action.navigationSnapshot);
260
258
  const matchedUrl = normalizeNavigationSnapshotMatchedUrl(pending.action.navigationSnapshot.pathname);
261
259
  return {
262
260
  displayUrl,
@@ -396,7 +394,8 @@ async function createPendingNavigationCommit(options) {
396
394
  currentPathname: options.currentState.navigationSnapshot.pathname,
397
395
  elements,
398
396
  nextPathname: options.navigationSnapshot.pathname,
399
- restored: options.restoredBfcacheIds
397
+ restored: options.restoredBfcacheIds,
398
+ reuseCurrent: options.reuseCurrentBfcacheIds
400
399
  }),
401
400
  ...cacheEntryReuseProof ? { cacheEntryReuseProof } : {},
402
401
  elements,
@@ -414,6 +413,7 @@ async function createPendingNavigationCommit(options) {
414
413
  previousNextUrl,
415
414
  renderId: options.renderId,
416
415
  rootLayoutTreePath: metadata.rootLayoutTreePath,
416
+ reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds ?? true,
417
417
  routeId: metadata.routeId,
418
418
  skippedLayoutIds: metadata.skippedLayoutIds,
419
419
  type: options.type
@@ -1,7 +1,7 @@
1
1
  import { normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
- import { mergeElements } from "../shims/slot.js";
4
3
  import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
4
+ import { mergeElements } from "../shims/slot.js";
5
5
  import { createPendingNavigationCommit, preserveBfcacheIdsForMergedElements, resolvePendingNavigationCommitDispositionDecision } from "./app-browser-state.js";
6
6
  //#region src/server/app-browser-visible-commit.ts
7
7
  const approvedVisibleCommitBrand = Symbol("ApprovedVisibleCommit");
@@ -56,29 +56,31 @@ function reduceApprovedVisibleCommitState(state, commit) {
56
56
  switch (action.type) {
57
57
  case "traverse":
58
58
  case "navigate": {
59
+ const preserveElementIds = action.reuseCurrentBfcacheIds ? commit.decision.preserveElementIds : [];
60
+ const preservePreviousSlotIds = action.reuseCurrentBfcacheIds ? commit.decision.preservePreviousSlotIds : [];
59
61
  const mergedElements = mergeElements(state.elements, action.elements, {
60
- clearAbsentSlots: action.type === "traverse",
61
- preserveAbsentSlots: commit.decision.preserveAbsentSlots,
62
- preserveElementIds: commit.decision.preserveElementIds,
63
- preservePreviousSlotIds: commit.decision.preservePreviousSlotIds
62
+ clearAbsentSlots: action.type === "traverse" || !action.reuseCurrentBfcacheIds,
63
+ preserveAbsentSlots: action.reuseCurrentBfcacheIds && commit.decision.preserveAbsentSlots,
64
+ preserveElementIds,
65
+ preservePreviousSlotIds
64
66
  });
65
67
  return commitVisibleRouterState(state, {
66
68
  bfcacheIds: preserveBfcacheIdsForMergedElements({
67
69
  elements: mergedElements,
68
70
  next: action.bfcacheIds,
69
- previous: state.bfcacheIds
71
+ previous: action.reuseCurrentBfcacheIds ? state.bfcacheIds : {}
70
72
  }),
71
73
  elements: mergedElements,
72
74
  interception: action.interception,
73
75
  interceptionContext: action.interceptionContext,
74
- layoutFlags: mergeLayoutFlags(state.layoutFlags, action.layoutFlags, commit.decision.preserveElementIds),
76
+ layoutFlags: mergeLayoutFlags(state.layoutFlags, action.layoutFlags, preserveElementIds),
75
77
  layoutIds: action.layoutIds,
76
78
  navigationSnapshot: action.navigationSnapshot,
77
79
  previousNextUrl: action.previousNextUrl,
78
80
  renderId: action.renderId,
79
81
  rootLayoutTreePath: action.rootLayoutTreePath,
80
82
  routeId: action.routeId,
81
- slotBindings: mergeSlotBindings(state.slotBindings, action.slotBindings, action.layoutIds, commit.decision.preservePreviousSlotIds)
83
+ slotBindings: mergeSlotBindings(state.slotBindings, action.slotBindings, action.layoutIds, preservePreviousSlotIds)
82
84
  }, action.operation);
83
85
  }
84
86
  case "replace": return commitVisibleRouterState(state, {
@@ -52,7 +52,8 @@ type AppFallbackRendererOptions<TModule extends AppPageModule = AppPageModule> =
52
52
  loadGlobalNotFoundModule?: (() => Promise<TModule | null | undefined>) | null;
53
53
  makeThenableParams: (params: AppPageParams) => unknown;
54
54
  metadataRoutes: MetadataFileRoute[]; /** Configured next.config `basePath`, threaded into file-based metadata href emission. */
55
- basePath?: string;
55
+ basePath?: string; /** Configured next.config `trailingSlash`, threaded into canonical URL rendering. */
56
+ trailingSlash?: boolean;
56
57
  resolveChildSegments: (routeSegments: readonly string[], treePosition: number, params: AppPageParams) => string[];
57
58
  rootBoundaries: AppFallbackRendererRootBoundaries<TModule>;
58
59
  rscRenderer: (element: ReactNode | AppElements, options: {
@@ -7,7 +7,7 @@ const EMPTY_MW_CTX = {
7
7
  status: null
8
8
  };
9
9
  function createAppFallbackRenderer(options) {
10
- const { basePath = "", clearRequestContext, createRscOnErrorHandler: buildRscOnErrorHandler, fontProviders, getNavigationContext, globalErrorModule, loadGlobalNotFoundModule, makeThenableParams, metadataRoutes, resolveChildSegments, rootBoundaries, rscRenderer, sanitizer, ssrLoader } = options;
10
+ const { basePath = "", clearRequestContext, createRscOnErrorHandler: buildRscOnErrorHandler, fontProviders, getNavigationContext, globalErrorModule, loadGlobalNotFoundModule, makeThenableParams, metadataRoutes, resolveChildSegments, rootBoundaries, rscRenderer, sanitizer, ssrLoader, trailingSlash } = options;
11
11
  const { rootForbiddenModule, rootLayouts, rootNotFoundModule, rootUnauthorizedModule } = rootBoundaries;
12
12
  const effectiveGlobalErrorModule = globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE;
13
13
  const effectiveRootNotFoundModule = rootNotFoundModule ?? DEFAULT_NOT_FOUND_MODULE;
@@ -58,6 +58,7 @@ function createAppFallbackRenderer(options) {
58
58
  }
59
59
  return renderAppPageHttpAccessFallback({
60
60
  basePath,
61
+ trailingSlash,
61
62
  boundaryComponent: opts?.boundaryComponent ?? null,
62
63
  boundaryModule: opts?.boundaryModule ?? null,
63
64
  buildFontLinkHeader: fontProviders.buildFontLinkHeader,
@@ -96,6 +97,7 @@ function createAppFallbackRenderer(options) {
96
97
  renderErrorBoundary(route, error, isRscRequest, request, matchedParams, scriptNonce, middlewareContext, callContext) {
97
98
  return renderAppPageErrorBoundary({
98
99
  basePath,
100
+ trailingSlash,
99
101
  buildFontLinkHeader: fontProviders.buildFontLinkHeader,
100
102
  clearRequestContext,
101
103
  createRscOnErrorHandler(pathname, routePath) {
@@ -10,6 +10,50 @@ type HistoryTraversalIntent = {
10
10
  historyState: unknown;
11
11
  targetHistoryIndex: number | null;
12
12
  };
13
+ type HistoryStateSnapshotRestoreDecision<TState> = {
14
+ kind: "restore";
15
+ state: TState;
16
+ targetHistoryIndex: number;
17
+ } | {
18
+ kind: "skip";
19
+ reason: "guarded" | "missing-history-index" | "missing-snapshot" | "stale-bfcache-version";
20
+ targetHistoryIndex: number | null;
21
+ };
22
+ declare class HistoryStateSnapshotCache<TState> {
23
+ #private;
24
+ constructor(options: {
25
+ maxEntries: number;
26
+ });
27
+ clear(): void;
28
+ remember(options: {
29
+ bfcacheVersion: number;
30
+ historyIndex: number | null;
31
+ state: TState;
32
+ }): void;
33
+ resolveRestore(options: {
34
+ currentBfcacheVersion: number;
35
+ guarded: boolean;
36
+ historyState: unknown;
37
+ }): HistoryStateSnapshotRestoreDecision<TState>;
38
+ }
39
+ declare class RestorableClientStateController<TState> {
40
+ #private;
41
+ constructor(options: {
42
+ initialHistoryState: unknown;
43
+ maxHistoryStateSnapshots: number;
44
+ });
45
+ get currentBfcacheVersion(): number;
46
+ beginCacheInvalidationGuard(): () => void;
47
+ isCacheInvalidationGuarded(): boolean;
48
+ isCurrentBfcacheVersion(historyState: unknown): boolean;
49
+ readCurrentBfcacheVersionHistoryIds(historyState: unknown): BfcacheIdMap | null;
50
+ invalidateClientState(): void;
51
+ rememberHistoryStateSnapshot(options: {
52
+ historyIndex: number | null;
53
+ state: TState;
54
+ }): void;
55
+ resolveHistoryStateSnapshotRestore(historyState: unknown): HistoryStateSnapshotRestoreDecision<TState>;
56
+ }
13
57
  declare function createHistoryStateWithPreviousNextUrl(state: unknown, previousNextUrl: string | null): HistoryStateRecord | null;
14
58
  declare function createHistoryStateWithNavigationMetadata(state: unknown, metadata: {
15
59
  bfcacheIds?: BfcacheIdMap | null;
@@ -39,4 +83,4 @@ declare function resolveHistoryTraversalIntent(options: {
39
83
  historyState: unknown;
40
84
  }): HistoryTraversalIntent;
41
85
  //#endregion
42
- export { BfcacheIdMap, HistoryTraversalIntent, createExternalHistoryStatePreservingMetadata, createHashOnlyHistoryStatePreservingNavigationMetadata, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isBfcacheSegmentId, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent };
86
+ export { BfcacheIdMap, HistoryStateSnapshotCache, HistoryTraversalIntent, RestorableClientStateController, createExternalHistoryStatePreservingMetadata, createHashOnlyHistoryStatePreservingNavigationMetadata, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isBfcacheSegmentId, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent };
@@ -6,6 +6,114 @@ const VINEXT_PREVIOUS_NEXT_URL_HISTORY_STATE_KEY = "__vinext_previousNextUrl";
6
6
  const VINEXT_HISTORY_INDEX_HISTORY_STATE_KEY = "__vinext_historyIndex";
7
7
  const VINEXT_BFCACHE_IDS_HISTORY_STATE_KEY = "__vinext_bfcacheIds";
8
8
  const VINEXT_BFCACHE_VERSION_HISTORY_STATE_KEY = "__vinext_bfcacheVersion";
9
+ var HistoryStateSnapshotCache = class {
10
+ #maxEntries;
11
+ #snapshots = /* @__PURE__ */ new Map();
12
+ constructor(options) {
13
+ this.#maxEntries = options.maxEntries;
14
+ }
15
+ clear() {
16
+ this.#snapshots.clear();
17
+ }
18
+ remember(options) {
19
+ if (options.historyIndex === null) return;
20
+ this.#snapshots.delete(options.historyIndex);
21
+ this.#snapshots.set(options.historyIndex, {
22
+ bfcacheVersion: options.bfcacheVersion,
23
+ state: options.state
24
+ });
25
+ if (this.#snapshots.size <= this.#maxEntries) return;
26
+ const oldestIndex = this.#snapshots.keys().next().value;
27
+ if (typeof oldestIndex === "number") this.#snapshots.delete(oldestIndex);
28
+ }
29
+ resolveRestore(options) {
30
+ const targetHistoryIndex = readHistoryStateTraversalIndex(options.historyState);
31
+ if (targetHistoryIndex === null) return {
32
+ kind: "skip",
33
+ reason: "missing-history-index",
34
+ targetHistoryIndex
35
+ };
36
+ const snapshot = this.#snapshots.get(targetHistoryIndex);
37
+ if (!snapshot) return {
38
+ kind: "skip",
39
+ reason: "missing-snapshot",
40
+ targetHistoryIndex
41
+ };
42
+ if (options.guarded) return {
43
+ kind: "skip",
44
+ reason: "guarded",
45
+ targetHistoryIndex
46
+ };
47
+ if (snapshot.bfcacheVersion !== options.currentBfcacheVersion) {
48
+ this.#snapshots.delete(targetHistoryIndex);
49
+ return {
50
+ kind: "skip",
51
+ reason: "stale-bfcache-version",
52
+ targetHistoryIndex
53
+ };
54
+ }
55
+ return {
56
+ kind: "restore",
57
+ state: snapshot.state,
58
+ targetHistoryIndex
59
+ };
60
+ }
61
+ };
62
+ var RestorableClientStateController = class {
63
+ #currentBfcacheVersion;
64
+ #pendingCacheInvalidationGuards = 0;
65
+ #snapshots;
66
+ constructor(options) {
67
+ const initialHistoryBfcacheVersion = readHistoryStateBfcacheVersion(options.initialHistoryState);
68
+ this.#currentBfcacheVersion = initialHistoryBfcacheVersion === null ? 0 : initialHistoryBfcacheVersion + 1;
69
+ this.#snapshots = new HistoryStateSnapshotCache({ maxEntries: options.maxHistoryStateSnapshots });
70
+ }
71
+ get currentBfcacheVersion() {
72
+ return this.#currentBfcacheVersion;
73
+ }
74
+ beginCacheInvalidationGuard() {
75
+ this.#pendingCacheInvalidationGuards += 1;
76
+ let released = false;
77
+ return () => {
78
+ if (released) return;
79
+ released = true;
80
+ this.#pendingCacheInvalidationGuards = Math.max(0, this.#pendingCacheInvalidationGuards - 1);
81
+ };
82
+ }
83
+ isCacheInvalidationGuarded() {
84
+ return this.#pendingCacheInvalidationGuards > 0;
85
+ }
86
+ isCurrentBfcacheVersion(historyState) {
87
+ return isHistoryStateBfcacheVersionCurrent(historyState, this.#currentBfcacheVersion);
88
+ }
89
+ readCurrentBfcacheVersionHistoryIds(historyState) {
90
+ if (this.isCacheInvalidationGuarded()) return null;
91
+ const ids = readHistoryStateBfcacheIds(historyState);
92
+ if (ids === null) return null;
93
+ return this.isCurrentBfcacheVersion(historyState) ? ids : null;
94
+ }
95
+ #invalidateBfcacheIds() {
96
+ this.#currentBfcacheVersion += 1;
97
+ }
98
+ invalidateClientState() {
99
+ this.#snapshots.clear();
100
+ this.#invalidateBfcacheIds();
101
+ }
102
+ rememberHistoryStateSnapshot(options) {
103
+ this.#snapshots.remember({
104
+ bfcacheVersion: this.#currentBfcacheVersion,
105
+ historyIndex: options.historyIndex,
106
+ state: options.state
107
+ });
108
+ }
109
+ resolveHistoryStateSnapshotRestore(historyState) {
110
+ return this.#snapshots.resolveRestore({
111
+ currentBfcacheVersion: this.#currentBfcacheVersion,
112
+ guarded: this.isCacheInvalidationGuarded(),
113
+ historyState
114
+ });
115
+ }
116
+ };
9
117
  function cloneHistoryState(state) {
10
118
  if (!state || typeof state !== "object") return {};
11
119
  const nextState = {};
@@ -112,4 +220,4 @@ function resolveHistoryTraversalIntent(options) {
112
220
  };
113
221
  }
114
222
  //#endregion
115
- export { createExternalHistoryStatePreservingMetadata, createHashOnlyHistoryStatePreservingNavigationMetadata, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isBfcacheSegmentId, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent };
223
+ export { HistoryStateSnapshotCache, RestorableClientStateController, createExternalHistoryStatePreservingMetadata, createHashOnlyHistoryStatePreservingNavigationMetadata, createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isBfcacheSegmentId, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent };
@@ -115,6 +115,7 @@ async function applyAppMiddleware(options) {
115
115
  if (!forwarded.applied) {
116
116
  const result = await executeMiddleware({
117
117
  basePath: options.basePath,
118
+ hadBasePath: true,
118
119
  i18nConfig: options.i18nConfig,
119
120
  isDataRequest: options.isDataRequest,
120
121
  isProxy: options.isProxy,
@@ -1,5 +1,6 @@
1
1
  import { buildParams, decodeMatchedParams, splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { stripBasePath } from "../utils/base-path.js";
3
+ import { matchRoutePattern } from "../routing/route-pattern.js";
3
4
  import { isUnknownRecord } from "../utils/record.js";
4
5
  import { AppElementsWire } from "./app-elements-wire.js";
5
6
  import "./app-elements.js";
@@ -135,6 +136,20 @@ function matchOptimisticRouteManifestRoute(options) {
135
136
  decodeMatchedParams(match.params);
136
137
  return match;
137
138
  }
139
+ function mergeParams(target, source) {
140
+ for (const [key, value] of Object.entries(source)) target[key] = value;
141
+ }
142
+ function resolveOptimisticNavigationParams(options) {
143
+ const navigationParams = { ...options.match.params };
144
+ for (const binding of options.routeManifest.segmentGraph.slotBindings.values()) {
145
+ if (binding.routeId !== options.match.route.id || binding.state !== "active") continue;
146
+ const patternParts = binding.slotPatternParts;
147
+ if (!patternParts) continue;
148
+ const matched = matchRoutePattern(options.urlParts, patternParts);
149
+ if (matched) mergeParams(navigationParams, matched);
150
+ }
151
+ return navigationParams;
152
+ }
138
153
  function elementHasSuspenseFallback(value, depth = 0) {
139
154
  if (depth > 100) return false;
140
155
  if (Array.isArray(value)) return value.some((entry) => elementHasSuspenseFallback(entry, depth + 1));
@@ -183,6 +198,8 @@ function createOptimisticRouteElements(template) {
183
198
  }
184
199
  function resolveOptimisticNavigationPayload(options) {
185
200
  if (options.interceptionContext !== null) return null;
201
+ const urlParts = hrefToRouteParts(options.href, options.basePath);
202
+ if (urlParts === null) return null;
186
203
  const match = matchOptimisticRouteManifestRoute({
187
204
  basePath: options.basePath,
188
205
  href: options.href,
@@ -198,7 +215,11 @@ function resolveOptimisticNavigationPayload(options) {
198
215
  if (template.mountedSlotsHeader !== options.mountedSlotsHeader) return null;
199
216
  return {
200
217
  elements: createOptimisticRouteElements(template),
201
- params: match.params,
218
+ params: resolveOptimisticNavigationParams({
219
+ match,
220
+ routeManifest: options.routeManifest,
221
+ urlParts
222
+ }),
202
223
  template
203
224
  };
204
225
  }
@@ -41,7 +41,8 @@ type AppPageBoundaryRenderCommonOptions<TModule extends AppPageModule = AppPageM
41
41
  makeThenableParams: (params: AppPageParams) => unknown;
42
42
  middlewareContext: AppPageMiddlewareContext;
43
43
  metadataRoutes: MetadataFileRoute[]; /** Configured next.config `basePath`, threaded into file-based metadata href emission. */
44
- basePath?: string;
44
+ basePath?: string; /** Configured next.config `trailingSlash`, threaded into canonical URL rendering. */
45
+ trailingSlash?: boolean;
45
46
  renderToReadableStream: (element: ReactNode | AppElements, options: {
46
47
  onError: AppPageBoundaryOnError;
47
48
  }) => ReadableStream<Uint8Array>;
@@ -1,15 +1,19 @@
1
1
  import { AppElementsWire } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
- import { ErrorBoundary } from "../shims/error-boundary.js";
3
+ import { isNavigationSignalError } from "../utils/navigation-signal.js";
4
+ import { ErrorBoundary, GlobalErrorBoundary } from "../shims/error-boundary.js";
4
5
  import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
5
6
  import { MetadataHead, ViewportHead } from "../shims/metadata.js";
7
+ import { resolveAppPageSpecialError } from "./app-page-execution.js";
6
8
  import { resolveAppPageHead } from "./app-page-head.js";
7
9
  import { createAppPageLayoutEntries } from "./app-page-route-wiring.js";
8
10
  import { buildClientHookErrorMessage } from "../shims/client-hook-error.js";
11
+ import DefaultGlobalError from "../shims/default-global-error.js";
9
12
  import { renderAppPageBoundaryResponse, resolveAppPageErrorBoundary, resolveAppPageHttpAccessBoundaryModule, wrapAppPageBoundaryElement } from "./app-page-boundary.js";
10
13
  import { createAppPageFontData, renderAppPageHtmlResponse } from "./app-page-stream.js";
11
14
  import { Fragment, createElement } from "react";
12
15
  //#region src/server/app-page-boundary-render.ts
16
+ const DEFAULT_GLOBAL_ERROR_COMPONENT = DefaultGlobalError;
13
17
  function getDefaultExport(module) {
14
18
  return module?.default ?? null;
15
19
  }
@@ -25,9 +29,12 @@ function wrapRenderedBoundaryElement(options) {
25
29
  makeThenableParams: options.makeThenableParams,
26
30
  matchedParams: options.matchedParams,
27
31
  renderErrorBoundary(GlobalErrorComponent, children) {
28
- return createElement(ErrorBoundary, {
29
- fallback: GlobalErrorComponent,
30
- children
32
+ return createElement(GlobalErrorBoundary, {
33
+ fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
34
+ children: createElement(ErrorBoundary, {
35
+ fallback: GlobalErrorComponent,
36
+ children
37
+ })
31
38
  });
32
39
  },
33
40
  renderLayout(LayoutComponent, children, asyncParams) {
@@ -161,7 +168,8 @@ async function renderAppPageHttpAccessFallback(options) {
161
168
  if (metadata) headElements.push(createElement(MetadataHead, {
162
169
  key: "metadata",
163
170
  metadata,
164
- pathname
171
+ pathname,
172
+ trailingSlash: options.trailingSlash
165
173
  }));
166
174
  headElements.push(createElement(ViewportHead, {
167
175
  key: "viewport",
@@ -224,7 +232,8 @@ async function renderAppPageErrorBoundary(options) {
224
232
  if (metadata) headElements.push(createElement(MetadataHead, {
225
233
  key: "metadata",
226
234
  metadata,
227
- pathname
235
+ pathname,
236
+ trailingSlash: options.trailingSlash
228
237
  }));
229
238
  headElements.push(createElement(ViewportHead, {
230
239
  key: "viewport",
@@ -233,22 +242,28 @@ async function renderAppPageErrorBoundary(options) {
233
242
  } catch (error) {
234
243
  console.error(`[vinext] App page error boundary head resolution failed for ${options.route?.pattern ?? pathname}:`, error);
235
244
  }
236
- const element = wrapRenderedBoundaryElement({
237
- element: createElement(Fragment, null, ...headElements, createElement(errorBoundary.component, { error: errorObject })),
238
- globalErrorModule: options.globalErrorModule,
239
- includeGlobalErrorBoundary: !errorBoundary.isGlobalError,
240
- isRscRequest: options.isRscRequest,
241
- layoutModules,
242
- layoutTreePositions: options.route?.layoutTreePositions,
243
- makeThenableParams: options.makeThenableParams,
244
- matchedParams,
245
- resolveChildSegments: options.resolveChildSegments,
246
- routeSegments: options.route?.routeSegments,
247
- skipLayoutWrapping: errorBoundary.isGlobalError
248
- });
249
- return renderAppPageBoundaryElementResponse({
245
+ const buildElement = (BoundaryComponent) => {
246
+ const boundaryElement = createElement(BoundaryComponent, { error: errorObject });
247
+ return wrapRenderedBoundaryElement({
248
+ element: createElement(Fragment, null, ...headElements, errorBoundary.isGlobalError ? createElement(GlobalErrorBoundary, {
249
+ fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
250
+ children: boundaryElement
251
+ }) : boundaryElement),
252
+ globalErrorModule: options.globalErrorModule,
253
+ includeGlobalErrorBoundary: !errorBoundary.isGlobalError,
254
+ isRscRequest: options.isRscRequest,
255
+ layoutModules,
256
+ layoutTreePositions: options.route?.layoutTreePositions,
257
+ makeThenableParams: options.makeThenableParams,
258
+ matchedParams,
259
+ resolveChildSegments: options.resolveChildSegments,
260
+ routeSegments: options.route?.routeSegments,
261
+ skipLayoutWrapping: errorBoundary.isGlobalError
262
+ });
263
+ };
264
+ const renderWith = (BoundaryComponent) => renderAppPageBoundaryElementResponse({
250
265
  ...options,
251
- element,
266
+ element: buildElement(BoundaryComponent),
252
267
  initialDevServerError: rawError,
253
268
  layoutModules,
254
269
  navigationParams: matchedParams,
@@ -256,6 +271,15 @@ async function renderAppPageErrorBoundary(options) {
256
271
  routePattern: options.route?.pattern,
257
272
  status: 200
258
273
  });
274
+ try {
275
+ return await renderWith(errorBoundary.component);
276
+ } catch (renderError) {
277
+ if (errorBoundary.isGlobalError && !isNavigationSignalError(renderError) && !resolveAppPageSpecialError(renderError)) {
278
+ console.error(`[vinext] global-error.tsx threw while rendering for ${options.route?.pattern ?? pathname}; falling back to the built-in default global-error:`, renderError);
279
+ return renderWith(DEFAULT_GLOBAL_ERROR_COMPONENT);
280
+ }
281
+ throw renderError;
282
+ }
259
283
  }
260
284
  const _clientHookPattern = /\b(useState|useEffect|useReducer|useRef|useContext|useLayoutEffect|useInsertionEffect|useSyncExternalStore|useTransition|useImperativeHandle|useDeferredValue|useActionState|useOptimistic|useEffectEvent)\b.*is not a function/;
261
285
  function rewriteClientHookError(error) {