@rangojs/router 0.24.3 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/README.md +1 -0
  2. package/dist/bin/rango.js +363 -39
  3. package/dist/testing/vitest.js +37 -4
  4. package/dist/types/browser/event-controller.d.ts +24 -5
  5. package/dist/types/browser/intercept-utils.d.ts +5 -2
  6. package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
  7. package/dist/types/browser/navigation-transaction.d.ts +5 -5
  8. package/dist/types/browser/partial-update.d.ts +21 -13
  9. package/dist/types/browser/prefetch/cache.d.ts +8 -0
  10. package/dist/types/browser/rango-marker.d.ts +7 -0
  11. package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
  12. package/dist/types/browser/react/use-handle.d.ts +20 -0
  13. package/dist/types/browser/react/use-href.d.ts +6 -0
  14. package/dist/types/browser/react/use-router.d.ts +6 -1
  15. package/dist/types/browser/response-adapter.d.ts +5 -3
  16. package/dist/types/browser/scroll-restoration.d.ts +9 -3
  17. package/dist/types/browser/segment-reconciler.d.ts +10 -13
  18. package/dist/types/browser/types.d.ts +38 -8
  19. package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
  20. package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
  21. package/dist/types/cache/types.d.ts +8 -0
  22. package/dist/types/client-urls/client-root.d.ts +4 -1
  23. package/dist/types/client-urls/client-urls.d.ts +28 -2
  24. package/dist/types/client-urls/navigation.d.ts +6 -8
  25. package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
  26. package/dist/types/client-urls/server-projection.d.ts +20 -5
  27. package/dist/types/client-urls/types.d.ts +35 -17
  28. package/dist/types/client.d.ts +2 -2
  29. package/dist/types/client.rsc.d.ts +2 -2
  30. package/dist/types/decode-loader-results.d.ts +28 -7
  31. package/dist/types/href-client.d.ts +3 -1
  32. package/dist/types/index.rsc.d.ts +1 -1
  33. package/dist/types/loader-redirect.d.ts +2 -3
  34. package/dist/types/outlet-context.d.ts +7 -6
  35. package/dist/types/prefetch-markers.d.ts +14 -5
  36. package/dist/types/route-content-wrapper.d.ts +17 -14
  37. package/dist/types/route-definition/helpers-types.d.ts +7 -2
  38. package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
  39. package/dist/types/route-definition/rewrite.d.ts +48 -5
  40. package/dist/types/route-types.d.ts +2 -2
  41. package/dist/types/router/handler-context.d.ts +14 -2
  42. package/dist/types/router/intercept-resolution.d.ts +19 -46
  43. package/dist/types/router/lazy-includes.d.ts +7 -0
  44. package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
  45. package/dist/types/router/middleware.d.ts +0 -16
  46. package/dist/types/router/navigation-snapshot.d.ts +2 -0
  47. package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
  48. package/dist/types/router/revalidation.d.ts +8 -1
  49. package/dist/types/router/router-context.d.ts +4 -16
  50. package/dist/types/router/router-interfaces.d.ts +0 -6
  51. package/dist/types/router/router-options.d.ts +4 -7
  52. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
  53. package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
  54. package/dist/types/router/segment-resolution.d.ts +1 -1
  55. package/dist/types/router/segment-wrappers.d.ts +3 -5
  56. package/dist/types/router/substitute-pattern-params.d.ts +19 -2
  57. package/dist/types/router/tracing.d.ts +2 -2
  58. package/dist/types/rsc/full-payload.d.ts +14 -2
  59. package/dist/types/rsc/helpers.d.ts +8 -0
  60. package/dist/types/rsc/manifest-init.d.ts +20 -0
  61. package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
  62. package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
  63. package/dist/types/rsc/rewrite-passes.d.ts +12 -2
  64. package/dist/types/rsc/server-action.d.ts +6 -0
  65. package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
  66. package/dist/types/rsc/shell-capture.d.ts +7 -0
  67. package/dist/types/rsc/types.d.ts +5 -0
  68. package/dist/types/segment-system.d.ts +15 -0
  69. package/dist/types/server/context.d.ts +64 -4
  70. package/dist/types/server/request-context.d.ts +41 -4
  71. package/dist/types/server/request-headers.d.ts +12 -0
  72. package/dist/types/server/rewrite-trail.d.ts +8 -1
  73. package/dist/types/testing/e2e/index.d.ts +1 -1
  74. package/dist/types/testing/flight.entry.d.ts +1 -1
  75. package/dist/types/testing/index.d.ts +1 -1
  76. package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
  77. package/dist/types/testing/render-route.d.ts +21 -11
  78. package/dist/types/testing/run-loader.d.ts +7 -0
  79. package/dist/types/testing/run-middleware.d.ts +7 -0
  80. package/dist/types/testing/run-revalidate.d.ts +5 -4
  81. package/dist/types/testing/serve-shell-request.d.ts +41 -4
  82. package/dist/types/testing/shell-status.d.ts +17 -0
  83. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  84. package/dist/types/testing/vitest.d.ts +10 -1
  85. package/dist/types/thenable-status.d.ts +0 -6
  86. package/dist/types/types/handler-context.d.ts +12 -1
  87. package/dist/types/types/loader-types.d.ts +4 -3
  88. package/dist/types/types/route-entry.d.ts +2 -1
  89. package/dist/types/urls/path-helper-types.d.ts +4 -2
  90. package/dist/types/urls/path-helper.d.ts +5 -0
  91. package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
  92. package/dist/types/vite/discovery/state.d.ts +20 -0
  93. package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
  94. package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
  95. package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
  96. package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
  97. package/dist/vite/index.js +6816 -6547
  98. package/package.json +1 -1
  99. package/skills/app-structure/SKILL.md +255 -0
  100. package/skills/app-structure/content-site.md +333 -0
  101. package/skills/app-structure/e-commerce.md +435 -0
  102. package/skills/app-structure/large-app.md +885 -0
  103. package/skills/app-structure/multi-app.md +280 -0
  104. package/skills/breadcrumbs/SKILL.md +6 -1
  105. package/skills/caching/SKILL.md +3 -1
  106. package/skills/catalog.json +12 -0
  107. package/skills/client-urls/SKILL.md +195 -32
  108. package/skills/composability/SKILL.md +10 -8
  109. package/skills/handler-use/SKILL.md +48 -9
  110. package/skills/hooks/handle-and-actions.md +104 -0
  111. package/skills/hooks/navigation.md +24 -10
  112. package/skills/hooks/urls.md +1 -1
  113. package/skills/i18n/SKILL.md +2 -1
  114. package/skills/intercept/SKILL.md +60 -29
  115. package/skills/layout/SKILL.md +6 -3
  116. package/skills/links/SKILL.md +151 -17
  117. package/skills/loader/SKILL.md +32 -10
  118. package/skills/middleware/SKILL.md +111 -15
  119. package/skills/observability/SKILL.md +11 -0
  120. package/skills/parallel/SKILL.md +5 -5
  121. package/skills/ppr/SKILL.md +43 -8
  122. package/skills/prerender/SKILL.md +1 -2
  123. package/skills/rango/SKILL.md +3 -0
  124. package/skills/response-routes/SKILL.md +4 -1
  125. package/skills/scripts/SKILL.md +12 -1
  126. package/skills/server-actions/SKILL.md +60 -20
  127. package/skills/testing/SKILL.md +1 -0
  128. package/skills/testing/client-components.md +13 -10
  129. package/skills/testing/loader.md +25 -24
  130. package/skills/testing/middleware.md +11 -11
  131. package/skills/testing/response-routes.md +4 -1
  132. package/skills/testing/serve-shell-request.md +85 -21
  133. package/skills/ux-architecture/SKILL.md +980 -0
  134. package/skills/view-transitions/SKILL.md +43 -10
  135. package/src/__augment-tests__/augmented.check.ts +28 -0
  136. package/src/browser/event-controller.ts +44 -18
  137. package/src/browser/intercept-utils.ts +3 -2
  138. package/src/browser/merge-segment-loaders.ts +0 -85
  139. package/src/browser/navigation-bridge.ts +119 -98
  140. package/src/browser/navigation-client.ts +20 -15
  141. package/src/browser/navigation-store.ts +4 -2
  142. package/src/browser/navigation-transaction.ts +9 -8
  143. package/src/browser/partial-update.ts +90 -32
  144. package/src/browser/prefetch/cache.ts +8 -0
  145. package/src/browser/prefetch/fetch.ts +13 -3
  146. package/src/browser/rango-marker.ts +13 -0
  147. package/src/browser/react/NavigationProvider.tsx +25 -7
  148. package/src/browser/react/filter-segment-order.ts +13 -0
  149. package/src/browser/react/use-action.ts +9 -15
  150. package/src/browser/react/use-handle.ts +63 -71
  151. package/src/browser/react/use-href.tsx +24 -5
  152. package/src/browser/react/use-link-status.ts +1 -1
  153. package/src/browser/react/use-navigation.ts +30 -40
  154. package/src/browser/react/use-reverse.ts +4 -17
  155. package/src/browser/react/use-router.ts +37 -6
  156. package/src/browser/react/use-segments.ts +8 -12
  157. package/src/browser/response-adapter.ts +11 -5
  158. package/src/browser/rsc-router.tsx +3 -1
  159. package/src/browser/scroll-restoration.ts +34 -20
  160. package/src/browser/segment-reconciler.ts +11 -29
  161. package/src/browser/server-action-bridge.ts +52 -16
  162. package/src/browser/types.ts +40 -8
  163. package/src/build/generate-manifest.ts +12 -1
  164. package/src/build/route-types/codegen.ts +6 -2
  165. package/src/build/route-types/per-module-writer.ts +94 -61
  166. package/src/cache/cache-runtime.ts +9 -0
  167. package/src/cache/cache-scope.ts +2 -1
  168. package/src/cache/cf/cf-cache-store.ts +2 -0
  169. package/src/cache/cf/cf-shell-frame.ts +3 -0
  170. package/src/cache/types.ts +8 -0
  171. package/src/cache/vercel/vercel-cache-store.ts +6 -1
  172. package/src/client-urls/client-root.tsx +10 -5
  173. package/src/client-urls/client-urls.ts +246 -166
  174. package/src/client-urls/navigation.ts +45 -38
  175. package/src/client-urls/revalidate-registry.ts +12 -3
  176. package/src/client-urls/server-projection.ts +286 -75
  177. package/src/client-urls/types.ts +40 -16
  178. package/src/client.rsc.tsx +11 -1
  179. package/src/client.tsx +9 -1
  180. package/src/decode-loader-results.ts +64 -31
  181. package/src/href-client.ts +3 -1
  182. package/src/index.rsc.ts +1 -0
  183. package/src/index.ts +12 -4
  184. package/src/loader-redirect.tsx +2 -3
  185. package/src/outlet-context.ts +7 -6
  186. package/src/prefetch-markers.ts +15 -5
  187. package/src/reverse.ts +3 -3
  188. package/src/route-content-wrapper.tsx +19 -17
  189. package/src/route-definition/dsl-helpers.ts +27 -7
  190. package/src/route-definition/helpers-types.ts +7 -2
  191. package/src/route-definition/resolve-handler-use.ts +23 -10
  192. package/src/route-definition/rewrite.ts +86 -19
  193. package/src/route-types.ts +1 -2
  194. package/src/router/find-match.ts +37 -1
  195. package/src/router/handler-context.ts +85 -40
  196. package/src/router/intercept-resolution.ts +161 -307
  197. package/src/router/lazy-includes.ts +43 -27
  198. package/src/router/loader-resolution.ts +21 -1
  199. package/src/router/manifest.ts +6 -8
  200. package/src/router/match-api.ts +11 -1
  201. package/src/router/match-middleware/background-revalidation.ts +2 -4
  202. package/src/router/match-middleware/cache-lookup.ts +16 -20
  203. package/src/router/match-middleware/intercept-resolution.ts +41 -60
  204. package/src/router/match-result.ts +31 -2
  205. package/src/router/middleware.ts +6 -147
  206. package/src/router/navigation-snapshot.ts +38 -2
  207. package/src/router/prefetch-cache-ttl.ts +10 -18
  208. package/src/router/prerender-match.ts +2 -1
  209. package/src/router/revalidation.ts +24 -3
  210. package/src/router/router-context.ts +5 -18
  211. package/src/router/router-interfaces.ts +0 -7
  212. package/src/router/router-options.ts +4 -7
  213. package/src/router/segment-resolution/fresh.ts +6 -2
  214. package/src/router/segment-resolution/helpers.ts +7 -1
  215. package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
  216. package/src/router/segment-resolution/revalidation.ts +75 -35
  217. package/src/router/segment-resolution.ts +0 -1
  218. package/src/router/segment-wrappers.ts +8 -13
  219. package/src/router/substitute-pattern-params.ts +37 -2
  220. package/src/router/tracing.ts +2 -2
  221. package/src/router.ts +2 -9
  222. package/src/rsc/full-payload.ts +26 -4
  223. package/src/rsc/handler.ts +83 -41
  224. package/src/rsc/helpers.ts +13 -0
  225. package/src/rsc/manifest-init.ts +56 -3
  226. package/src/rsc/prefetch-render-error.ts +38 -0
  227. package/src/rsc/progressive-enhancement.ts +161 -211
  228. package/src/rsc/rewrite-passes.ts +56 -9
  229. package/src/rsc/rsc-rendering.ts +79 -23
  230. package/src/rsc/server-action.ts +25 -2
  231. package/src/rsc/shell-capture-constants.ts +22 -0
  232. package/src/rsc/shell-capture.ts +11 -0
  233. package/src/rsc/types.ts +5 -0
  234. package/src/segment-system.tsx +125 -119
  235. package/src/server/context.ts +96 -5
  236. package/src/server/handle-store.ts +3 -2
  237. package/src/server/request-context.ts +42 -3
  238. package/src/server/request-headers.ts +32 -0
  239. package/src/server/rewrite-trail.ts +55 -9
  240. package/src/ssr/ssr-root.tsx +6 -10
  241. package/src/testing/dispatch.ts +4 -5
  242. package/src/testing/e2e/index.ts +1 -0
  243. package/src/testing/flight.entry.ts +1 -0
  244. package/src/testing/index.ts +1 -0
  245. package/src/testing/internal/client-urls-projection.ts +31 -11
  246. package/src/testing/render-route.tsx +83 -40
  247. package/src/testing/run-loader.ts +26 -2
  248. package/src/testing/run-middleware.ts +10 -0
  249. package/src/testing/run-revalidate.ts +13 -6
  250. package/src/testing/serve-shell-request.ts +77 -4
  251. package/src/testing/shell-status.ts +40 -0
  252. package/src/testing/vitest-stubs/version.ts +1 -0
  253. package/src/testing/vitest.ts +74 -7
  254. package/src/thenable-status.ts +0 -11
  255. package/src/types/handler-context.ts +12 -1
  256. package/src/types/loader-types.ts +4 -3
  257. package/src/types/route-entry.ts +2 -1
  258. package/src/urls/include-helper.ts +38 -11
  259. package/src/urls/path-helper-types.ts +4 -2
  260. package/src/urls/path-helper.ts +16 -3
  261. package/src/vite/discovery/client-urls-projection.ts +205 -16
  262. package/src/vite/discovery/discover-routers.ts +2 -2
  263. package/src/vite/discovery/prerender-collection.ts +2 -6
  264. package/src/vite/discovery/state.ts +26 -0
  265. package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
  266. package/src/vite/plugins/expose-internal-ids.ts +46 -40
  267. package/src/vite/plugins/hoist/source.ts +8 -7
  268. package/src/vite/plugins/transition-when-hoist.ts +1 -0
  269. package/src/vite/plugins/version-plugin.ts +7 -0
  270. package/src/vite/plugins/version.d.ts +7 -0
  271. package/src/vite/plugins/virtual-entries.ts +20 -0
  272. package/src/vite/rango.ts +4 -1
  273. package/src/vite/router-discovery.ts +39 -7
  274. package/src/vite/utils/prerender-utils.ts +17 -0
@@ -43,6 +43,7 @@ import {
43
43
  deferUnit,
44
44
  fillSends,
45
45
  getPrefetchDeferral,
46
+ interceptFillSkips,
46
47
  registerDeferredLoader,
47
48
  } from "./prefetch-deferral.js";
48
49
  import {
@@ -58,6 +59,7 @@ import { applyViewTransitionDefault } from "./view-transition-default.js";
58
59
  import { isLayoutLoaderCopy } from "../../client-urls/layout-loader-copies.js";
59
60
  import { getRouterContext } from "../router-context.js";
60
61
  import { observeEvent, observeSegmentHandler } from "../instrument.js";
62
+ import { withReverseScope } from "../handler-context.js";
61
63
  import { runInSegmentTagScope } from "../../cache/cache-tag.js";
62
64
  import { trackStreamedHandler } from "./streamed-handler-boundary.js";
63
65
  import {
@@ -156,7 +158,7 @@ const deferredLoaderRuns = new WeakMap<ResolvedSegment, () => unknown>();
156
158
  * awaits; a streamed handler that reads the loader later is not seen, and
157
159
  * that loader runs again in the fill.
158
160
  */
159
- function deliverStartedLoaders<TEnv>(
161
+ export function deliverStartedLoaders<TEnv>(
160
162
  ctx: HandlerContext<any, TEnv>,
161
163
  segments: ResolvedSegment[],
162
164
  ): void {
@@ -180,14 +182,51 @@ function deliverStartedLoaders<TEnv>(
180
182
  // ---------------------------------------------------------------------------
181
183
 
182
184
  /**
183
- * Resolve loaders with revalidation awareness (for partial rendering).
184
- * Returns both segments to render AND all matched segment IDs.
185
+ * Whose loaders a resolveOwnerLoaders call resolves: a route entry's (or the
186
+ * entry its loaders are inherited into), or an intercept's, whose loaders are
187
+ * loader segments of its slot (`${parent}.${slot}D${i}.${$$id}`).
185
188
  */
186
- export async function resolveLoadersWithRevalidation<TEnv>(
189
+ export interface LoaderOwner {
190
+ readonly loaders: readonly LoaderEntry[];
191
+ /** The segments' namespace: the entry id, or `intercept:${routeName}`. */
192
+ readonly namespace: string;
193
+ /** The segment id the loader ids extend and the handle pushes file under. */
194
+ readonly segmentPrefix: string;
195
+ /** Where a loader error renders (wrapLoaderPromise). */
196
+ readonly boundary: EntryData;
197
+ /**
198
+ * resolveLoaderData's PPR bake/seed key for every loader (loader-cache.ts).
199
+ * False for an intercept: never a document.
200
+ */
201
+ readonly bakeLane: boolean;
202
+ readonly traceSource: "loader" | "intercept-loader";
203
+ }
204
+
205
+ /** A route entry as a LoaderOwner, under `shortCodeOverride` when inherited. */
206
+ export function ownerOf(
187
207
  entry: EntryData,
208
+ shortCodeOverride?: string,
209
+ ): LoaderOwner {
210
+ return {
211
+ loaders: entry.loader ?? [],
212
+ namespace: entry.id,
213
+ segmentPrefix: shortCodeOverride ?? entry.shortCode,
214
+ boundary: entry,
215
+ bakeLane: !entryLoadingMasksLoaders(entry.loading),
216
+ traceSource: "loader",
217
+ };
218
+ }
219
+
220
+ /**
221
+ * The partial lane's one loader funnel: each loader is a segment, run when
222
+ * the client lacks it, asked its revalidate() when held, kept outright in a
223
+ * fill (fillSends), deferred in a prefetch that defers it (defersLoader).
224
+ */
225
+ export async function resolveOwnerLoaders<TEnv>(
226
+ owner: LoaderOwner,
188
227
  ctx: HandlerContext<any, TEnv>,
189
228
  belongsToRoute: boolean,
190
- clientSegmentIds: Set<string>,
229
+ clientSegmentIds: ReadonlySet<string>,
191
230
  prevParams: Record<string, string>,
192
231
  request: Request,
193
232
  prevUrl: URL,
@@ -195,13 +234,12 @@ export async function resolveLoadersWithRevalidation<TEnv>(
195
234
  routeKey: string,
196
235
  deps: SegmentResolutionDeps<TEnv>,
197
236
  actionContext?: ActionContext,
198
- shortCodeOverride?: string,
199
237
  stale?: boolean,
200
238
  ): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
201
- const loaderEntries = entry.loader ?? [];
239
+ const loaderEntries = owner.loaders;
202
240
  if (loaderEntries.length === 0) return { segments: [], matchedIds: [] };
203
241
 
204
- const shortCode = shortCodeOverride ?? entry.shortCode;
242
+ const shortCode = owner.segmentPrefix;
205
243
 
206
244
  // Pin `_currentSegmentId` to the OWNING entry before the kickoffs below —
207
245
  // createLoaderExecutor captures it synchronously for ctx.use(Handle) push
@@ -238,7 +276,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
238
276
  segmentId,
239
277
  segmentType: "loader",
240
278
  belongsToRoute,
241
- source: "loader",
279
+ source: owner.traceSource,
242
280
  defaultShouldRevalidate: true,
243
281
  finalShouldRevalidate: true,
244
282
  reason: "new-segment",
@@ -249,7 +287,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
249
287
 
250
288
  const dummySegment: ResolvedSegment = {
251
289
  id: segmentId,
252
- namespace: entry.id,
290
+ namespace: owner.namespace,
253
291
  type: "loader",
254
292
  index,
255
293
  component: null,
@@ -268,14 +306,14 @@ export async function resolveLoadersWithRevalidation<TEnv>(
268
306
  prevUrl,
269
307
  nextUrl,
270
308
  revalidations: loaderRevalidateFns.map((fn, j) => ({
271
- name: `loader-revalidate${j}`,
309
+ name: `${owner.traceSource}-revalidate${j}`,
272
310
  fn,
273
311
  })),
274
312
  routeKey,
275
313
  context: ctx,
276
314
  actionContext,
277
315
  stale,
278
- traceSource: "loader",
316
+ traceSource: owner.traceSource,
279
317
  held: clientSegmentIds,
280
318
  });
281
319
  },
@@ -300,7 +338,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
300
338
  // Outside capture the key only activates the _shellLoaderSeed overlay,
301
339
  // which serveShellHit seeds for a document HIT and the PPR partial replay
302
340
  // (matchPartialWithPprReplay) seeds once its doc record hits.
303
- const bakeLane = !entryLoadingMasksLoaders(entry.loading);
341
+ const bakeLane = owner.bakeLane;
304
342
 
305
343
  const kickoff = (
306
344
  loaderEntry: LoaderEntry,
@@ -319,6 +357,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
319
357
  bakeLane || loaderEntry.bake === true ? segmentId : null,
320
358
  ),
321
359
  loaderId,
360
+ loaderEntry.reverseScope,
322
361
  );
323
362
  const start = (
324
363
  loaderEntry: LoaderEntry,
@@ -327,7 +366,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
327
366
  ) =>
328
367
  deps.wrapLoaderPromise(
329
368
  kickoff(loaderEntry, segmentId, loaderId),
330
- entry,
369
+ owner.boundary,
331
370
  segmentId,
332
371
  ctx.pathname,
333
372
  errorContext,
@@ -338,7 +377,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
338
377
  ({ loaderEntry, loader, segmentId, index }) => {
339
378
  const base = {
340
379
  id: segmentId,
341
- namespace: entry.id,
380
+ namespace: owner.namespace,
342
381
  type: "loader" as const,
343
382
  index,
344
383
  component: null,
@@ -411,8 +450,8 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
411
450
  seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
412
451
  );
413
452
  if (!allAlreadySeen) {
414
- const { segments, matchedIds } = await resolveLoadersWithRevalidation(
415
- entry,
453
+ const { segments, matchedIds } = await resolveOwnerLoaders(
454
+ ownerOf(entry, shortCodeOverride),
416
455
  context,
417
456
  belongsToRoute,
418
457
  clientSegmentIds,
@@ -423,7 +462,6 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
423
462
  routeKey,
424
463
  deps,
425
464
  actionContext,
426
- shortCodeOverride,
427
465
  stale,
428
466
  );
429
467
  for (const seg of segments) {
@@ -454,8 +492,8 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
454
492
  entry.loader.length > 0 &&
455
493
  Object.keys(layoutEntry.parallel).length > 0
456
494
  ) {
457
- const inherited = await resolveLoadersWithRevalidation(
458
- entry,
495
+ const inherited = await resolveOwnerLoaders(
496
+ ownerOf(entry, layoutEntry.shortCode),
459
497
  context,
460
498
  childBelongsToRoute,
461
499
  clientSegmentIds,
@@ -466,7 +504,6 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
466
504
  routeKey,
467
505
  deps,
468
506
  actionContext,
469
- layoutEntry.shortCode,
470
507
  stale,
471
508
  );
472
509
  for (const seg of inherited.segments) {
@@ -594,10 +631,11 @@ async function resolveParallelSlotComponent<TEnv>(args: {
594
631
  (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
595
632
  parallelId;
596
633
  handlerRan = true;
634
+ const slotContext = withReverseScope(context, parallelEntry.reverseScope);
597
635
  if (hasLoadingFallback) {
598
636
  const result =
599
637
  typeof handler === "function"
600
- ? runInSegmentTagScope(parallelId, handler, context)
638
+ ? runInSegmentTagScope(parallelId, handler, slotContext)
601
639
  : handler;
602
640
  if (result instanceof Promise) {
603
641
  warnOnStreamedResponse(result, parallelId);
@@ -616,7 +654,7 @@ async function resolveParallelSlotComponent<TEnv>(args: {
616
654
  } else {
617
655
  component =
618
656
  typeof handler === "function"
619
- ? await runInSegmentTagScope(parallelId, handler, context)
657
+ ? await runInSegmentTagScope(parallelId, handler, slotContext)
620
658
  : handler;
621
659
  }
622
660
  }
@@ -697,8 +735,8 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
697
735
  // emitted either before or after the slot segment per loaderOrder.
698
736
  const resolveSlotLoaders = async () => {
699
737
  if (resolvedParallelEntries.has(parallelEntry.id)) return;
700
- const loaderResult = await resolveLoadersWithRevalidation(
701
- parallelEntry,
738
+ const loaderResult = await resolveOwnerLoaders(
739
+ ownerOf(parallelEntry, entry.shortCode),
702
740
  context,
703
741
  belongsToRoute,
704
742
  clientSegmentIds,
@@ -709,7 +747,6 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
709
747
  routeKey,
710
748
  deps,
711
749
  actionContext,
712
- entry.shortCode,
713
750
  stale,
714
751
  );
715
752
  segments.push(...loaderResult.segments);
@@ -1095,8 +1132,8 @@ export async function resolveSegmentWithRevalidation<TEnv>(
1095
1132
 
1096
1133
  const belongsToRoute = entry.type === "route";
1097
1134
 
1098
- const loaderResult = await resolveLoadersWithRevalidation(
1099
- entry,
1135
+ const loaderResult = await resolveOwnerLoaders(
1136
+ ownerOf(entry),
1100
1137
  context,
1101
1138
  belongsToRoute,
1102
1139
  clientSegmentIds,
@@ -1107,7 +1144,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
1107
1144
  routeKey,
1108
1145
  deps,
1109
1146
  actionContext,
1110
- undefined,
1111
1147
  stale,
1112
1148
  );
1113
1149
  segments.push(...loaderResult.segments);
@@ -1266,8 +1302,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
1266
1302
  const segments: ResolvedSegment[] = [];
1267
1303
  const matchedIds: string[] = [];
1268
1304
 
1269
- const loaderResult = await resolveLoadersWithRevalidation(
1270
- orphan,
1305
+ const loaderResult = await resolveOwnerLoaders(
1306
+ ownerOf(orphan),
1271
1307
  context,
1272
1308
  belongsToRoute,
1273
1309
  clientSegmentIds,
@@ -1278,7 +1314,6 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
1278
1314
  routeKey,
1279
1315
  deps,
1280
1316
  actionContext,
1281
- undefined,
1282
1317
  stale,
1283
1318
  );
1284
1319
  segments.push(...loaderResult.segments);
@@ -1292,8 +1327,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
1292
1327
  parentRouteEntry.loader.length > 0 &&
1293
1328
  Object.keys(orphan.parallel).length > 0
1294
1329
  ) {
1295
- const inheritedResult = await resolveLoadersWithRevalidation(
1296
- parentRouteEntry,
1330
+ const inheritedResult = await resolveOwnerLoaders(
1331
+ ownerOf(parentRouteEntry, orphan.shortCode),
1297
1332
  context,
1298
1333
  belongsToRoute,
1299
1334
  clientSegmentIds,
@@ -1304,7 +1339,6 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
1304
1339
  routeKey,
1305
1340
  deps,
1306
1341
  actionContext,
1307
- orphan.shortCode,
1308
1342
  stale,
1309
1343
  );
1310
1344
  // Tag as inherited so buildMatchResult can deduplicate when safe
@@ -1491,6 +1525,11 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
1491
1525
  latchPprHeaderScopeForEntries(entries, routeKey);
1492
1526
 
1493
1527
  const telemetry = getRouterContext()?.telemetry;
1528
+ const fillMatch = {
1529
+ handlerContext: context,
1530
+ isIntercept: !!interceptResult,
1531
+ clientSegmentSet,
1532
+ };
1494
1533
 
1495
1534
  for (const entry of entries) {
1496
1535
  if (entry.type === "route" && interceptResult) {
@@ -1508,6 +1547,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
1508
1547
  }
1509
1548
  continue;
1510
1549
  }
1550
+ if (interceptFillSkips(fillMatch, entry.shortCode)) continue;
1511
1551
 
1512
1552
  const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
1513
1553
  // Same latch as resolveAllSegments (fresh.ts), route-level cache() included.
@@ -14,7 +14,6 @@ export {
14
14
  resolveLoadersOnly,
15
15
  } from "./segment-resolution/fresh.js";
16
16
  export {
17
- resolveLoadersWithRevalidation,
18
17
  resolveLoadersOnlyWithRevalidation,
19
18
  buildEntryRevalidateMap,
20
19
  resolveParallelSegmentsWithRevalidation,
@@ -6,6 +6,7 @@ import type {
6
6
  } from "../types";
7
7
  import type { SegmentResolutionDeps } from "./types.js";
8
8
  import type { ResolveSegmentOptions } from "./segment-resolution.js";
9
+ import type { RevalidationContext } from "./router-context.js";
9
10
 
10
11
  import {
11
12
  resolveAllSegments as _resolveAllSegments,
@@ -96,19 +97,15 @@ export interface SegmentWrappers<TEnv = any> {
96
97
  context: HandlerContext<any, TEnv>,
97
98
  belongsToRoute?: boolean,
98
99
  revalidationContext?: any,
99
- options?: { skipMiddleware?: boolean },
100
+ options?: { background?: boolean },
100
101
  ) => Promise<ResolvedSegment[]>;
101
102
  resolveInterceptLoadersOnly: (
102
103
  interceptEntry: InterceptEntry,
103
104
  parentEntry: EntryData,
104
- params: Record<string, string>,
105
105
  context: HandlerContext<any, TEnv>,
106
- belongsToRoute?: boolean,
107
- revalidationContext?: any,
108
- ) => Promise<{
109
- loaderDataPromise: Promise<any[]> | any[];
110
- loaderIds: string[];
111
- } | null>;
106
+ belongsToRoute: boolean,
107
+ revalidationContext: RevalidationContext,
108
+ ) => Promise<ResolvedSegment[]>;
112
109
  }
113
110
 
114
111
  /**
@@ -250,7 +247,7 @@ export function createSegmentWrappers<TEnv = any>(
250
247
  context: HandlerContext<any, TEnv>,
251
248
  belongsToRoute: boolean = true,
252
249
  revalidationContext?: any,
253
- options?: { skipMiddleware?: boolean },
250
+ options?: { background?: boolean },
254
251
  ): ReturnType<typeof _resolveInterceptEntry> {
255
252
  return _resolveInterceptEntry(
256
253
  interceptEntry,
@@ -267,15 +264,13 @@ export function createSegmentWrappers<TEnv = any>(
267
264
  function resolveInterceptLoadersOnly(
268
265
  interceptEntry: InterceptEntry,
269
266
  parentEntry: EntryData,
270
- params: Record<string, string>,
271
267
  context: HandlerContext<any, TEnv>,
272
- belongsToRoute: boolean = true,
273
- revalidationContext: any,
268
+ belongsToRoute: boolean,
269
+ revalidationContext: RevalidationContext,
274
270
  ): ReturnType<typeof _resolveInterceptLoadersOnly> {
275
271
  return _resolveInterceptLoadersOnly(
276
272
  interceptEntry,
277
273
  parentEntry,
278
- params,
279
274
  context,
280
275
  belongsToRoute,
281
276
  segmentDeps,
@@ -20,8 +20,9 @@ import { parsePattern } from "./parse-pattern.js";
20
20
  * omit when absent.
21
21
  *
22
22
  * Shared by `ctx.reverse()` (server), `createReverse()` (typed runtime
23
- * helper), and `useReverse()` (client hook). The behavior must stay
24
- * identical across all three call sites.
23
+ * helper), `useReverse()` (client hook), and the include() mount fill of
24
+ * `useHref()` and relative `useRouter().push`. The behavior must stay
25
+ * identical across all of them.
25
26
  */
26
27
  export function substitutePatternParams(
27
28
  pattern: string,
@@ -73,3 +74,37 @@ export function substitutePatternParams(
73
74
  if (hasTrailingSlash && !result.endsWith("/")) result += "/";
74
75
  return result;
75
76
  }
77
+
78
+ /**
79
+ * Join an include() mount with a mount-relative pattern. The mount is the
80
+ * include PATTERN (`/:locale?`), so the result is a pattern too.
81
+ *
82
+ * `pattern === "/"` is the index of the local module: under a non-root mount
83
+ * it collapses, so `/` under `/blog` becomes `/blog`, not `/blog/`. This
84
+ * matches `ctx.reverse(".index")` and, for a mount without a trailing slash,
85
+ * the route pattern the server registers (applyUrlPrefix in
86
+ * urls/path-helper.ts).
87
+ */
88
+ export function joinMount(mount: string, pattern: string): string {
89
+ if (pattern === "/") {
90
+ if (mount === "" || mount === "/") return "/";
91
+ return mount.endsWith("/") ? mount.slice(0, -1) : mount;
92
+ }
93
+ const normalizedMount = mount === "/" ? "" : mount.replace(/\/+$/, "");
94
+ return normalizedMount + pattern;
95
+ }
96
+
97
+ /**
98
+ * Whether two matches of an include() mount pattern are in the same instance
99
+ * of the include: `/en/...` and `/de/...` under `/:locale` are not.
100
+ */
101
+ export function sameMountInstance(
102
+ mount: string,
103
+ a: Readonly<Record<string, string>>,
104
+ b: Readonly<Record<string, string>>,
105
+ ): boolean {
106
+ return (
107
+ substitutePatternParams(mount, a, mount) ===
108
+ substitutePatternParams(mount, b, mount)
109
+ );
110
+ }
@@ -24,8 +24,8 @@
24
24
  * registry; do not call traceSpan directly from new code.
25
25
  *
26
26
  * Phase coverage (all via observePhase): rango.request (span-only; handler:total
27
- * metered directly), rango.middleware (span-only incl. intercept middleware;
28
- * pre/post metered directly), rango.action (action:<id>; server-action
27
+ * metered directly), rango.middleware (span-only; pre/post metered directly),
28
+ * rango.action (action:<id>; server-action
29
29
  * execution, JS + no-JS/PE), rango.loader (loader:<id>; single metering site at
30
30
  * useLoader, plus the fetchable path), rango.handler (span-only, one per segment
31
31
  * route/layout handler execution; the handler:<id> perf metric is owned by the
package/src/router.ts CHANGED
@@ -276,14 +276,8 @@ export function createRouter<TEnv = any>(
276
276
  );
277
277
 
278
278
  // Resolve prefetch cache TTL (default: 300 seconds / 5 minutes). Clamps to a
279
- // non-negative integer and guards non-finite (NaN/Infinity) inputs so a
280
- // malformed `Cache-Control: max-age=NaN` can never reach the wire.
281
- const resolvedPrefetchCacheTTL = resolvePrefetchCacheTTL(
282
- prefetchCacheTTLOption,
283
- );
284
- const prefetchCacheTTL = resolvedPrefetchCacheTTL.ms;
285
- const prefetchCacheControl: string | false =
286
- resolvedPrefetchCacheTTL.cacheControl;
279
+ // non-negative integer and guards non-finite (NaN/Infinity) inputs.
280
+ const prefetchCacheTTL = resolvePrefetchCacheTTL(prefetchCacheTTLOption).ms;
287
281
 
288
282
  // Resolve client-side prefetch limits (in-memory cache size and queue
289
283
  // concurrency). Both are positive-integer counts; sub-1/non-finite inputs
@@ -1153,7 +1147,6 @@ export function createRouter<TEnv = any>(
1153
1147
  cacheProfiles: resolvedCacheProfiles,
1154
1148
 
1155
1149
  // Expose prefetch cache settings
1156
- prefetchCacheControl,
1157
1150
  prefetchCacheTTL,
1158
1151
  prefetchCacheSize,
1159
1152
  prefetchConcurrency,
@@ -15,6 +15,7 @@ import type { RequestContext } from "../server/request-context.js";
15
15
  import type { HandleStore } from "../server/handle-store.js";
16
16
  import type { Theme } from "../theme/types.js";
17
17
  import {
18
+ pathKey,
18
19
  rendersForSharedAudience,
19
20
  rewriteTrail,
20
21
  visitorUrl,
@@ -42,7 +43,7 @@ export function buildFullPayload(
42
43
  ): RscPayload {
43
44
  return {
44
45
  metadata: {
45
- pathname: renderedLocation(reqCtx, url).pathname,
46
+ ...payloadLocation(reqCtx, url),
46
47
  routerId: ctx.router.id,
47
48
  basename: ctx.router.basename,
48
49
  segments: attachTransitionWhen(m.segments, reqCtx),
@@ -95,7 +96,8 @@ export function buildFullPayload(
95
96
  * bar) agree on `usePathname()` and `useSearchParams()`. A render other
96
97
  * visitors may be served shows `url`, the target, as a direct visit does: a
97
98
  * shell capture, and a render the document cache stores (its key is the
98
- * target). A render that shows the visitor's URL marks
99
+ * target), except a visitor-keyed shell capture, whose key covers the
100
+ * visitor's URL. Any other render that shows the visitor's URL marks
99
101
  * `_payloadVisitorLocation`, and the document cache does not store it.
100
102
  */
101
103
  export function renderedLocation(
@@ -104,6 +106,7 @@ export function renderedLocation(
104
106
  RequestContext<any>,
105
107
  | "_variables"
106
108
  | "_shellCaptureRun"
109
+ | "_shellCaptureForVisitor"
107
110
  | "_documentCacheRender"
108
111
  | "_payloadVisitorLocation"
109
112
  | "res"
@@ -111,13 +114,32 @@ export function renderedLocation(
111
114
  | undefined,
112
115
  url: URL,
113
116
  ): URL {
114
- if (!reqCtx || !rewriteTrail(reqCtx) || rendersForSharedAudience(reqCtx)) {
115
- return url;
117
+ if (!reqCtx || !rewriteTrail(reqCtx)) return url;
118
+ if (rendersForSharedAudience(reqCtx)) {
119
+ return reqCtx._shellCaptureForVisitor ? visitorUrl(reqCtx, url) : url;
116
120
  }
117
121
  reqCtx._payloadVisitorLocation = true;
118
122
  return visitorUrl(reqCtx, url);
119
123
  }
120
124
 
125
+ /**
126
+ * A payload's `pathname` (renderedLocation) and, when middleware rewrote the
127
+ * request, `rewriteTarget`: the path and search the route rendered (the
128
+ * trail's last URL). The browser keeps it per history entry and sends it
129
+ * back as `_rsc_rt`, so the next request from the page diffs against the
130
+ * route that rendered it (navigation-snapshot.ts resolveNavigation, #1144).
131
+ */
132
+ export function payloadLocation(
133
+ reqCtx: Parameters<typeof renderedLocation>[0],
134
+ url: URL,
135
+ ): { pathname: string; rewriteTarget?: string } {
136
+ const pathname = renderedLocation(reqCtx, url).pathname;
137
+ const trail = rewriteTrail(reqCtx);
138
+ return trail
139
+ ? { pathname, rewriteTarget: pathKey(new URL(trail[trail.length - 1]!)) }
140
+ : { pathname };
141
+ }
142
+
121
143
  /**
122
144
  * The theme a document payload carries as `metadata.initialTheme` (the
123
145
  * ThemeProvider's first state), read with no identity guard: it is the