@rangojs/router 0.27.0 → 0.29.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 (243) hide show
  1. package/dist/bin/rango.js +1 -1
  2. package/dist/types/__internal.d.ts +7 -1
  3. package/dist/types/browser/action-fence.d.ts +2 -1
  4. package/dist/types/browser/event-controller.d.ts +8 -2
  5. package/dist/types/browser/history-state.d.ts +3 -9
  6. package/dist/types/browser/navigation-store.d.ts +8 -6
  7. package/dist/types/browser/partial-update.d.ts +11 -0
  8. package/dist/types/browser/react/filter-segment-order.d.ts +2 -2
  9. package/dist/types/browser/react/use-action.d.ts +7 -2
  10. package/dist/types/browser/react/use-handle.d.ts +19 -16
  11. package/dist/types/browser/react/use-link-status.d.ts +5 -2
  12. package/dist/types/browser/segment-reconciler.d.ts +32 -4
  13. package/dist/types/browser/types.d.ts +24 -5
  14. package/dist/types/browser/wire-protocol.d.ts +26 -0
  15. package/dist/types/cache/cache-key-utils.d.ts +11 -5
  16. package/dist/types/cache/cache-scope.d.ts +34 -22
  17. package/dist/types/cache/cf/cf-cache-constants.d.ts +2 -2
  18. package/dist/types/cache/cf/cf-cache-store.d.ts +47 -65
  19. package/dist/types/cache/cf/cf-kv-utils.d.ts +1 -1
  20. package/dist/types/cache/handle-snapshot.d.ts +9 -4
  21. package/dist/types/cache/segment-codec.d.ts +14 -0
  22. package/dist/types/cache/shell-snapshot.d.ts +1 -1
  23. package/dist/types/cache/taint.d.ts +5 -3
  24. package/dist/types/cache/types.d.ts +7 -3
  25. package/dist/types/cache/vercel/vercel-cache-store.d.ts +8 -6
  26. package/dist/types/prefetch-markers.d.ts +5 -14
  27. package/dist/types/prerender/store.d.ts +31 -0
  28. package/dist/types/prerender/warm-request.d.ts +3 -3
  29. package/dist/types/prerender.d.ts +2 -2
  30. package/dist/types/route-name.d.ts +5 -0
  31. package/dist/types/router/error-handling.d.ts +11 -5
  32. package/dist/types/router/instrument.d.ts +4 -3
  33. package/dist/types/router/intercept-resolution.d.ts +25 -9
  34. package/dist/types/router/match-context.d.ts +6 -11
  35. package/dist/types/router/match-handlers.d.ts +2 -6
  36. package/dist/types/router/match-middleware/background-revalidation.d.ts +13 -22
  37. package/dist/types/router/match-middleware/cache-lookup.d.ts +34 -86
  38. package/dist/types/router/match-middleware/segment-resolution.d.ts +8 -6
  39. package/dist/types/router/navigation-snapshot.d.ts +7 -0
  40. package/dist/types/router/prerender-match.d.ts +21 -6
  41. package/dist/types/router/preview-match.d.ts +2 -7
  42. package/dist/types/router/request-classification.d.ts +5 -0
  43. package/dist/types/router/router-context.d.ts +7 -46
  44. package/dist/types/router/router-interfaces.d.ts +8 -14
  45. package/dist/types/router/segment-resolution/helpers.d.ts +19 -3
  46. package/dist/types/router/segment-resolution/loader-cache.d.ts +3 -3
  47. package/dist/types/router/segment-resolution/loader-mask.d.ts +4 -4
  48. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +20 -18
  49. package/dist/types/router/segment-resolution/resolution-pass.d.ts +95 -0
  50. package/dist/types/router/segment-resolution/revalidation.d.ts +82 -31
  51. package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +2 -0
  52. package/dist/types/router/segment-resolution.d.ts +1 -2
  53. package/dist/types/router/segment-wrappers.d.ts +11 -48
  54. package/dist/types/router/telemetry.d.ts +2 -0
  55. package/dist/types/router/types.d.ts +11 -1
  56. package/dist/types/rsc/full-payload.d.ts +2 -2
  57. package/dist/types/rsc/handler-context.d.ts +6 -6
  58. package/dist/types/rsc/json-route-result.d.ts +5 -9
  59. package/dist/types/rsc/request-stages.d.ts +203 -0
  60. package/dist/types/rsc/response-cache-serve.d.ts +7 -12
  61. package/dist/types/rsc/response-route-handler.d.ts +2 -2
  62. package/dist/types/rsc/routine-plan.d.ts +1 -1
  63. package/dist/types/rsc/server-action.d.ts +2 -2
  64. package/dist/types/rsc/shell-capture-constants.d.ts +1 -1
  65. package/dist/types/rsc/shell-capture.d.ts +132 -15
  66. package/dist/types/rsc/shell-serve.d.ts +37 -12
  67. package/dist/types/server/context.d.ts +9 -8
  68. package/dist/types/server/handle-store.d.ts +16 -0
  69. package/dist/types/server/render-pass.d.ts +72 -0
  70. package/dist/types/server/request-context.d.ts +53 -104
  71. package/dist/types/server/rewrite-trail.d.ts +2 -2
  72. package/dist/types/testing/dispatch.d.ts +35 -18
  73. package/dist/types/testing/render-route.d.ts +43 -8
  74. package/dist/types/testing/serve-shell-request.d.ts +8 -1
  75. package/dist/types/types/cache-types.d.ts +6 -3
  76. package/dist/types/types/error-types.d.ts +30 -3
  77. package/dist/types/types/handler-context.d.ts +9 -1
  78. package/dist/types/types/segments.d.ts +1 -1
  79. package/dist/types/vite/discovery/build-discovery-phase.d.ts +40 -0
  80. package/dist/types/vite/discovery/build-env.d.ts +22 -0
  81. package/dist/types/vite/discovery/bundle-scan-phase.d.ts +12 -0
  82. package/dist/types/vite/discovery/dev-prerender-cache.d.ts +3 -2
  83. package/dist/types/vite/discovery/dev-server-phase.d.ts +26 -0
  84. package/dist/types/vite/discovery/discovery-errors.d.ts +1 -1
  85. package/dist/types/vite/discovery/state.d.ts +12 -0
  86. package/dist/types/vite/plugins/version-plugin.d.ts +1 -1
  87. package/dist/types/vite/plugins/virtual-entries.d.ts +1 -1
  88. package/dist/types/vite/router-discovery.d.ts +8 -47
  89. package/dist/types/vite/temp-rsc-server.d.ts +39 -0
  90. package/dist/types/vite/utils/prerender-utils.d.ts +9 -0
  91. package/dist/types/wire-params.d.ts +39 -0
  92. package/dist/vite/index.js +3822 -3727
  93. package/package.json +1 -1
  94. package/skills/cache-guide/SKILL.md +8 -5
  95. package/skills/caching/SKILL.md +37 -18
  96. package/skills/client-urls/SKILL.md +4 -2
  97. package/skills/document-cache/SKILL.md +13 -1
  98. package/skills/handler-use/SKILL.md +1 -1
  99. package/skills/hooks/SKILL.md +1 -1
  100. package/skills/hooks/data.md +8 -1
  101. package/skills/hooks/handle-and-actions.md +33 -2
  102. package/skills/hooks/navigation.md +121 -15
  103. package/skills/hooks/state.md +6 -4
  104. package/skills/hooks/urls.md +16 -5
  105. package/skills/intercept/SKILL.md +23 -7
  106. package/skills/middleware/SKILL.md +18 -0
  107. package/skills/observability/SKILL.md +30 -0
  108. package/skills/ppr/SKILL.md +18 -3
  109. package/skills/prerender/SKILL.md +39 -15
  110. package/skills/rango/SKILL.md +5 -1
  111. package/skills/server-actions/SKILL.md +15 -1
  112. package/skills/testing/client-components.md +25 -18
  113. package/skills/testing/response-routes.md +6 -4
  114. package/skills/testing/serve-shell-request.md +7 -7
  115. package/skills/use-cache/SKILL.md +46 -8
  116. package/skills/view-transitions/SKILL.md +1 -1
  117. package/src/__internal.ts +7 -1
  118. package/src/browser/action-fence.ts +2 -1
  119. package/src/browser/connection-warmup.ts +3 -1
  120. package/src/browser/event-controller.ts +37 -13
  121. package/src/browser/history-state.ts +6 -12
  122. package/src/browser/navigation-bridge.ts +59 -26
  123. package/src/browser/navigation-client.ts +20 -25
  124. package/src/browser/navigation-store.ts +25 -27
  125. package/src/browser/navigation-transaction.ts +4 -1
  126. package/src/browser/network-error-handler.ts +4 -0
  127. package/src/browser/partial-update.ts +33 -6
  128. package/src/browser/prefetch/cache.ts +2 -1
  129. package/src/browser/prefetch/fetch.ts +8 -16
  130. package/src/browser/prefetch/queue.ts +4 -4
  131. package/src/browser/react/NavigationProvider.tsx +2 -1
  132. package/src/browser/react/filter-segment-order.ts +2 -2
  133. package/src/browser/react/use-action.ts +24 -48
  134. package/src/browser/react/use-handle.ts +22 -18
  135. package/src/browser/react/use-link-status.ts +48 -60
  136. package/src/browser/react/use-navigation.ts +92 -41
  137. package/src/browser/rsc-router.tsx +1 -0
  138. package/src/browser/segment-reconciler.ts +109 -53
  139. package/src/browser/server-action-bridge.ts +81 -61
  140. package/src/browser/types.ts +25 -5
  141. package/src/browser/wire-protocol.ts +43 -0
  142. package/src/cache/cache-key-utils.ts +11 -5
  143. package/src/cache/cache-runtime.ts +132 -75
  144. package/src/cache/cache-scope.ts +110 -64
  145. package/src/cache/cf/cf-cache-constants.ts +2 -2
  146. package/src/cache/cf/cf-cache-store.ts +486 -874
  147. package/src/cache/cf/cf-kv-utils.ts +1 -1
  148. package/src/cache/document-cache.ts +64 -46
  149. package/src/cache/handle-snapshot.ts +19 -7
  150. package/src/cache/read-through-swr.ts +20 -18
  151. package/src/cache/segment-codec.ts +55 -0
  152. package/src/cache/shell-snapshot.ts +2 -2
  153. package/src/cache/tag-invalidation.ts +6 -2
  154. package/src/cache/taint.ts +5 -3
  155. package/src/cache/types.ts +7 -3
  156. package/src/cache/vercel/vercel-cache-store.ts +243 -310
  157. package/src/handles/pushed-by.ts +1 -1
  158. package/src/prefetch-markers.ts +5 -16
  159. package/src/prerender/store.ts +76 -0
  160. package/src/prerender/warm-request.ts +8 -4
  161. package/src/prerender.ts +2 -2
  162. package/src/route-name.ts +8 -0
  163. package/src/router/error-handling.ts +22 -10
  164. package/src/router/instrument.ts +4 -3
  165. package/src/router/intercept-resolution.ts +82 -75
  166. package/src/router/loader-resolution.ts +5 -4
  167. package/src/router/match-api.ts +5 -5
  168. package/src/router/match-context.ts +23 -12
  169. package/src/router/match-handlers.ts +8 -20
  170. package/src/router/match-middleware/background-revalidation.ts +46 -57
  171. package/src/router/match-middleware/cache-lookup.ts +677 -726
  172. package/src/router/match-middleware/cache-store.ts +4 -8
  173. package/src/router/match-middleware/intercept-resolution.ts +3 -23
  174. package/src/router/match-middleware/segment-resolution.ts +34 -61
  175. package/src/router/middleware.ts +69 -11
  176. package/src/router/navigation-snapshot.ts +14 -4
  177. package/src/router/prerender-match.ts +100 -44
  178. package/src/router/preview-match.ts +6 -41
  179. package/src/router/request-classification.ts +13 -7
  180. package/src/router/route-snapshot.ts +2 -5
  181. package/src/router/router-context.ts +10 -95
  182. package/src/router/router-interfaces.ts +8 -14
  183. package/src/router/segment-resolution/helpers.ts +49 -13
  184. package/src/router/segment-resolution/loader-cache.ts +15 -9
  185. package/src/router/segment-resolution/loader-mask.ts +5 -5
  186. package/src/router/segment-resolution/prefetch-deferral.ts +30 -27
  187. package/src/router/segment-resolution/resolution-pass.ts +139 -0
  188. package/src/router/segment-resolution/revalidation.ts +939 -789
  189. package/src/router/segment-resolution/streamed-handler-boundary.ts +3 -0
  190. package/src/router/segment-resolution.ts +4 -13
  191. package/src/router/segment-wrappers.ts +51 -269
  192. package/src/router/telemetry.ts +5 -0
  193. package/src/router/types.ts +11 -1
  194. package/src/router.ts +19 -17
  195. package/src/rsc/full-payload.ts +6 -5
  196. package/src/rsc/handler-context.ts +8 -14
  197. package/src/rsc/handler.ts +126 -507
  198. package/src/rsc/json-route-result.ts +5 -9
  199. package/src/rsc/loader-fetch.ts +6 -5
  200. package/src/rsc/progressive-enhancement.ts +5 -0
  201. package/src/rsc/request-stages.ts +759 -0
  202. package/src/rsc/response-cache-serve.ts +10 -14
  203. package/src/rsc/response-route-handler.ts +13 -5
  204. package/src/rsc/rewrite-passes.ts +2 -1
  205. package/src/rsc/routine-plan.ts +1 -1
  206. package/src/rsc/rsc-rendering.ts +164 -349
  207. package/src/rsc/server-action.ts +87 -113
  208. package/src/rsc/shell-capture-constants.ts +1 -1
  209. package/src/rsc/shell-capture.ts +275 -227
  210. package/src/rsc/shell-serve.ts +121 -18
  211. package/src/rsc/ssr-setup.ts +5 -4
  212. package/src/segment-system.tsx +3 -3
  213. package/src/server/context.ts +22 -25
  214. package/src/server/handle-store.ts +55 -0
  215. package/src/server/render-pass.ts +291 -0
  216. package/src/server/request-context.ts +75 -115
  217. package/src/server/rewrite-trail.ts +6 -4
  218. package/src/ssr/index.tsx +1 -1
  219. package/src/testing/dispatch.ts +226 -710
  220. package/src/testing/e2e/page-helpers.ts +2 -2
  221. package/src/testing/render-route.tsx +199 -13
  222. package/src/testing/serve-shell-request.ts +36 -40
  223. package/src/types/cache-types.ts +6 -3
  224. package/src/types/error-types.ts +30 -3
  225. package/src/types/handler-context.ts +12 -1
  226. package/src/types/segments.ts +1 -1
  227. package/src/vite/discovery/build-discovery-phase.ts +204 -0
  228. package/src/vite/discovery/build-env.ts +148 -0
  229. package/src/vite/discovery/bundle-scan-phase.ts +128 -0
  230. package/src/vite/discovery/dev-prerender-cache.ts +8 -3
  231. package/src/vite/discovery/dev-server-phase.ts +1776 -0
  232. package/src/vite/discovery/discover-routers.ts +1 -1
  233. package/src/vite/discovery/discovery-errors.ts +1 -1
  234. package/src/vite/discovery/prerender-collection.ts +24 -39
  235. package/src/vite/discovery/state.ts +15 -0
  236. package/src/vite/plugins/version-plugin.ts +1 -1
  237. package/src/vite/plugins/virtual-entries.ts +1 -1
  238. package/src/vite/router-discovery.ts +29 -2428
  239. package/src/vite/temp-rsc-server.ts +223 -0
  240. package/src/vite/utils/prerender-utils.ts +47 -0
  241. package/src/wire-params.ts +40 -0
  242. package/dist/types/router/segment-resolution/fresh.d.ts +0 -72
  243. package/src/router/segment-resolution/fresh.ts +0 -945
package/dist/bin/rango.js CHANGED
@@ -1644,7 +1644,7 @@ var init_package = __esm({
1644
1644
  "package.json"() {
1645
1645
  package_default = {
1646
1646
  name: "@rangojs/router",
1647
- version: "0.27.0",
1647
+ version: "0.29.0",
1648
1648
  description: "Django-inspired RSC router with composable URL patterns",
1649
1649
  keywords: [
1650
1650
  "react",
@@ -53,7 +53,13 @@ export type { CachedEntryData, CacheGetResult, SerializedSegmentData, CacheDefau
53
53
  * @internal
54
54
  * Router context for AsyncLocalStorage.
55
55
  */
56
- export type { RouterContext, RevalidationContext, InterceptResult, } from "./router/router-context.js";
56
+ export type { RouterContext, InterceptResult, } from "./router/router-context.js";
57
+ /**
58
+ * @internal
59
+ * The request side of a revalidating resolution pass (RouterContext's
60
+ * revalidating resolvers take it).
61
+ */
62
+ export type { ResolutionPassInput } from "./router/segment-resolution/resolution-pass.js";
57
63
  /**
58
64
  * @internal
59
65
  * Route match context during pipeline processing.
@@ -20,7 +20,8 @@
20
20
  *
21
21
  * The drop runs the batch's follow-up: when an action of the batch did not
22
22
  * land on the page on screen, the action bridge refetches that page once,
23
- * whole, after the navigation the user started, unless an intercept is on
23
+ * whole (or whole above an action's error boundary on screen, which it
24
+ * holds), after the navigation the user started, unless an intercept is on
24
25
  * screen (server-action-bridge.ts followUp; the rules are R1-R7 in
25
26
  * docs/internal/execution-model.md).
26
27
  *
@@ -59,6 +59,11 @@ export interface ActionEntry {
59
59
  startedAt: number;
60
60
  /** Whether action processing is complete (may still be streaming) */
61
61
  completed?: boolean;
62
+ /**
63
+ * Set when a later call started (see startAction): "id" hides this call
64
+ * from every reader, "name" from readers that match by export name.
65
+ */
66
+ supersededBy?: "id" | "name";
62
67
  }
63
68
  /**
64
69
  * Derived navigation state (computed from source of truth)
@@ -70,7 +75,7 @@ export interface DerivedNavigationState {
70
75
  isStreaming: boolean;
71
76
  /** Whether a navigation is active (fetching or streaming, before commit) */
72
77
  isNavigating: boolean;
73
- /** Current committed location */
78
+ /** The location of the last page flushed to React (useNavigation keeps the page on screen) */
74
79
  location: NavigationLocation;
75
80
  /** URL being navigated to (null if idle) */
76
81
  pendingUrl: string | null;
@@ -207,7 +212,8 @@ export interface EventController {
207
212
  startAction(actionId: string, args: unknown[],
208
213
  /** Originating history entry key; scopes location-state arbitration. */
209
214
  cohort?: string): ActionHandle;
210
- abortAllActions(): void;
215
+ /** Abort every action in flight except `except` (an ActionHandle id). */
216
+ abortAllActions(except?: string): void;
211
217
  getState(): DerivedNavigationState;
212
218
  getActionState(actionId: string): TrackedActionState;
213
219
  getLocation(): NavigationLocation;
@@ -21,15 +21,9 @@ export declare function hasLocationState(state: unknown): boolean;
21
21
  */
22
22
  export declare function pushHistoryWithIdx(state: Record<string, unknown> | null, url: string, replace: boolean): void;
23
23
  /**
24
- * The forced-MISS reload marker (`SHELL_MISS_PARAM` in rsc/shell-serve.ts,
25
- * which the browser bundle must not import): a degraded PPR shell HIT
26
- * reloads the page with it, and the server renders a marked request like a
27
- * cache miss.
28
- */
29
- export declare const SHELL_MISS_MARKER: string;
30
- /**
31
- * Drop the forced-MISS marker from the address bar at boot, keeping the
32
- * history entry's state (initBrowserApp calls it before any location read).
24
+ * Drop the forced-MISS marker (`WIRE.shellMiss`, rsc/shell-serve.ts) from the
25
+ * address bar at boot, keeping the history entry's state (initBrowserApp
26
+ * calls it before any location read).
33
27
  * Left in place, a refresh or a shared link would stay off the shell and the
34
28
  * app's search params would carry it. The server strips the same marker from
35
29
  * the request before rendering (shell-serve.ts withoutShellMissMarker), so
@@ -24,16 +24,18 @@ export declare function liveHandleDataForCache(eventController: Pick<EventContro
24
24
  * Options for generating a history key
25
25
  */
26
26
  export interface HistoryKeyOptions {
27
- /** If true, append :intercept suffix to differentiate intercept entries */
27
+ /** Key an intercept entry (apart from the plain entry for the URL). */
28
28
  intercept?: boolean;
29
+ /** The page the intercept was opened over (history.state.sourceUrl). */
30
+ interceptSource?: string;
29
31
  }
30
32
  /**
31
- * Generate a cache key from a URL.
32
- * Uses pathname + search (query params) directly as the key.
33
- * Hash fragments (#) are excluded since they don't affect server data.
33
+ * Generate a cache key from a URL: pathname + search. Hash fragments (#) are
34
+ * excluded since they don't affect server data.
34
35
  *
35
- * For intercept routes, append `:intercept` suffix to cache them separately
36
- * from non-intercept versions of the same URL.
36
+ * An intercept entry is keyed `<url>:intercept:<source>`: its cached tree
37
+ * includes the page under the intercept. hasInterceptCache relies on the
38
+ * `:intercept:` separator.
37
39
  */
38
40
  export declare function generateHistoryKey(url?: string, options?: HistoryKeyOptions): string;
39
41
  /**
@@ -112,18 +112,29 @@ export type UpdateMode = {
112
112
  * transition({ when }) sees kind "revalidate", not "replace".
113
113
  */
114
114
  refresh?: boolean;
115
+ /** The cross-tab refresh's held boundary (see `action`). */
116
+ boundary?: string;
115
117
  } | {
116
118
  type: "leave-intercept";
117
119
  interceptSourceUrl?: string;
120
+ boundary?: never;
118
121
  } | {
119
122
  type: "stale-revalidation";
120
123
  interceptSourceUrl?: string;
124
+ /** Back/Forward's held boundary (see `action`). */
125
+ boundary?: string;
121
126
  } | {
122
127
  type: "action";
123
128
  interceptSourceUrl?: string;
124
129
  actionId?: string;
125
130
  /** The action's FormData and result, for transition({ when }) `action`. */
126
131
  action?: TransitionWhenActionInput;
132
+ /**
133
+ * An action's error boundary the page shows (actionBoundaryAt): the
134
+ * server renders above it, its cached segment stays, and the page
135
+ * holds only the ids above it.
136
+ */
137
+ boundary?: string;
127
138
  };
128
139
  /**
129
140
  * Type for the fetchPartialUpdate function. In `action` mode it resolves with
@@ -26,8 +26,8 @@ export declare function loaderParentId(id: string): string;
26
26
  * 2. Place each parallel slot id (contains ".@") immediately after its
27
27
  * parent layout/route id. Raw segment-resolution emission order does NOT
28
28
  * guarantee this: route-mounted parallels are resolved/pushed BEFORE the
29
- * route handler's segment is appended (see fresh.ts:resolveSegment for
30
- * routes, and revalidation.ts ~915-919), so matched can read
29
+ * route handler's segment is appended (see resolveSegment in
30
+ * segment-resolution/revalidation.ts), so matched can read
31
31
  * `[..., R0.@panel, R0]`. collectHandleData consumes segmentOrder verbatim
32
32
  * with later-wins semantics, so without normalization the route handler's
33
33
  * Meta would override the slot's more-specific Meta — backwards.
@@ -18,14 +18,19 @@ export type ServerActionFunction = ((...args: any[]) => Promise<any>) & {
18
18
  * Features:
19
19
  * - Tracks action lifecycle: idle → loading → streaming → idle
20
20
  * - Captures result/error locally (React handles cleanup)
21
- * - If multiple actions fire, tracks only the last one
21
+ * - If the same action fires several times, reports the latest call to
22
+ * start. An earlier call that settles later changes nothing it reports;
23
+ * a latest call that was aborted reports idle with nothing. One call at a
24
+ * time, not one per argument: a reader filtering on `payload` cannot track
25
+ * an earlier call once a later one starts.
22
26
  * - Supports selector pattern like useNavigation
23
27
  *
24
28
  * Matching behavior:
25
29
  * - **Function reference**: Uses full $$id for exact matching. This is precise
26
30
  * and distinguishes between actions with the same name in different files.
27
31
  * - **String**: Matches by suffix (action name after #). This is convenient
28
- * but may be ambiguous if multiple files export the same action name.
32
+ * but may be ambiguous if multiple files export the same action name: it
33
+ * follows the latest call of any action with that export name.
29
34
  *
30
35
  * @param action - Either a server action function or a string action name.
31
36
  * - **Function**: Must be directly imported in the client component.
@@ -21,21 +21,24 @@ import type { RouteStateSignal } from "../event-controller.js";
21
21
  export declare function useHandle<T, A>(handle: Handle<T, A>): Rango.FlightSerialize<A>;
22
22
  export declare function useHandle<T, A, S>(handle: Handle<T, A>, selector: (data: Rango.FlightSerialize<A>) => S): S;
23
23
  /**
24
- * Route state the controller's handle notifications drive (useHandle,
25
- * useSegments) commits with the page it belongs to. NavigationProvider
26
- * flushes the controller inside every payload update, so a value known then
27
- * is set in the page's lane. A page handed over urgently (signal "release")
28
- * takes a value that arrives before it commits the same way: set at default
29
- * priority, it renders in the page's lane, and the page's effects read it.
30
- * A page in a transition (signal "anchor") does not: until it commits
24
+ * Route state the controller's handle notifications drive commits with the
25
+ * page it belongs to. NavigationProvider flushes the controller inside every
26
+ * payload update, with signal "anchor" for a page in a transition and
27
+ * "release" for an urgent one, so the page's own change is set in the page's
28
+ * lane and the page's effects read it. From the anchor until the page commits
31
29
  * (releaseRouteState() from the provider's layout effect, signal "release"),
32
- * a new value waits, and is applied in the commit's task, right after the
33
- * page's own commit. In the page's lane, a value that suspends in a revealed
34
- * <Suspense> (a crumb's content) would hold the page; in a lane of its own,
35
- * it commits over the page being left and takes the page's transition types
36
- * (#1165); and a transition entangled with the page misses its commit when it
37
- * is set after React rendered the page, before its view transition's
38
- * mutation, and starts a view transition of its own. A value shallow-equal to
39
- * the last one sets nothing, so a reader renders only when its value moves.
30
+ * a later change waits and is applied in the commit's task, right after the
31
+ * page's own commit: set in a lane of its own, it would commit over the page
32
+ * being left (#1165).
33
+ *
34
+ * `waitForRelease`, set by useHandle only: the anchor's own change waits too,
35
+ * so a handle value lands right after its page's commit, never in its lane. A
36
+ * handle value can carry a promise that suspends, and in the page's lane one
37
+ * that suspends in a revealed <Suspense> would hold the page. Nothing
38
+ * useSegments reads can suspend, so it leaves it off and its path and
39
+ * location commit with their page.
40
+ *
41
+ * A value shallow-equal to the last one sets nothing, so a reader renders only
42
+ * when its value moves.
40
43
  */
41
- export declare function useCommitAnchoredState<T>(init: () => T): [T, (value: T, signal?: RouteStateSignal) => void];
44
+ export declare function useCommitAnchoredState<T>(init: () => T, waitForRelease?: boolean): [T, (value: T, signal?: RouteStateSignal) => void];
@@ -14,8 +14,11 @@ export interface LinkStatus {
14
14
  /**
15
15
  * Hook to track the pending state of a Link component
16
16
  *
17
- * Must be used inside a Link component. Returns `{ pending: true }`
18
- * when navigation to this link's destination is in progress.
17
+ * Must be used inside a Link component. Returns `{ pending: true }` while a
18
+ * navigation to the link's destination is in flight, until the page it
19
+ * brings commits: under transition() while React holds that page, and
20
+ * through a server redirect until the target page commits. A newer
21
+ * navigation ends it at once.
19
22
  *
20
23
  * Useful for showing inline loading indicators on individual links.
21
24
  *
@@ -62,10 +62,38 @@ export interface ReconcileResult {
62
62
  */
63
63
  export declare function reconcileSegments(input: ReconcileInput): ReconcileResult;
64
64
  /**
65
- * Reconcile error segments with cached segments.
65
+ * Reconcile an error-boundary response with cached segments.
66
66
  *
67
- * For error responses, the server returns the error boundary segment.
68
- * This function overlays error segments onto the full cached tree,
69
- * preserving sibling layouts that aren't in the error parent chain.
67
+ * The server returns the error boundary segment and, above it, what the
68
+ * action's revalidation re-ran (rsc/server-action.ts). This function overlays
69
+ * them onto the full cached tree, preserving sibling layouts that aren't in
70
+ * the error parent chain. The error segment replaces its cached copy; every
71
+ * other segment merges as an action's does (mergeCached).
70
72
  */
71
73
  export declare function reconcileErrorSegments(cachedSegments: ResolvedSegment[], errorSegments: ResolvedSegment[]): ReconcileResult;
74
+ /**
75
+ * The ids a page holds once an action's error boundary `boundary` shows: the
76
+ * ones above it. Nothing at or below the boundary ran after the action's
77
+ * write (its response and the follow-up cut there), so the next request must
78
+ * not hold it. ShortCodes nest, so those ids start with the boundary's: the
79
+ * boundary, its loaders (`${id}D…`), its slots and every entry under it. A
80
+ * sibling with a longer index (`…R10` beside `…R1`) is kept.
81
+ */
82
+ export declare function idsAboveBoundary(ids: string[], boundary: string | undefined): string[];
83
+ /**
84
+ * The error segment of every action error boundary committed, by identity
85
+ * (server-action-bridge.ts adds and deletes). An entry shows one while its
86
+ * cached list still holds that object: a later commit on the entry replaces
87
+ * the list, and a refetch that holds the boundary keeps it. Weak, so a
88
+ * replaced boundary is not kept alive. Two routers never share a segment
89
+ * object. No hook reads it.
90
+ */
91
+ export declare const actionBoundaries: WeakSet<ResolvedSegment>;
92
+ /**
93
+ * The index of the deepest action error boundary in `segments`, -1 for none.
94
+ * It is that action's outcome, which no GET reproduces: a background refetch
95
+ * of the page (the follow-up, Back/Forward's stale revalidation, the
96
+ * cross-tab refresh) holds it and renders what is above it. At 0 nothing is
97
+ * above it.
98
+ */
99
+ export declare function actionBoundaryAt(segments: readonly ResolvedSegment[]): number;
@@ -191,11 +191,24 @@ export interface InflightAction {
191
191
  * @internal This type is an implementation detail. Use PublicNavigationState instead.
192
192
  */
193
193
  export interface NavigationState {
194
- /** Navigation lifecycle state (idle or loading during navigation) */
194
+ /**
195
+ * Lifecycle state: `"loading"` while a navigation is in flight and while a
196
+ * server action runs, `"idle"` otherwise
197
+ */
195
198
  state: "idle" | "loading";
196
- /** Whether RSC data is currently streaming (initial load or navigation) */
199
+ /**
200
+ * Whether RSC data is currently streaming: a navigation's response, or a
201
+ * server action's response (initial load, navigation or action)
202
+ */
197
203
  isStreaming: boolean;
198
- /** Current location */
204
+ /**
205
+ * True while a navigation is in flight (fetching or streaming, before its
206
+ * commit); false during a server action, whose `state` is still `"loading"`.
207
+ * Drive a navigation progress bar from this and an action spinner from
208
+ * `state`.
209
+ */
210
+ isNavigating: boolean;
211
+ /** The page on screen; moves when the next page shows (`pendingUrl` is the page being fetched) */
199
212
  location: NavigationLocation;
200
213
  /** URL being navigated to (null when idle) */
201
214
  pendingUrl: string | null;
@@ -450,6 +463,8 @@ export interface NavigateOptionsInternal extends Omit<NavigateOptions, "state">
450
463
  _skipCache?: boolean;
451
464
  /** Server redirects already followed by this navigation chain (0 for a user navigation) */
452
465
  _redirectHops?: number;
466
+ /** The URL a redirected navigation chain started from: a link to it stays pending (useLinkStatus) */
467
+ _redirectFrom?: string;
453
468
  }
454
469
  /**
455
470
  * Options for useRouter push/replace methods.
@@ -558,7 +573,8 @@ export interface NavigationStore {
558
573
  */
559
574
  handlesPending?: boolean;
560
575
  } | undefined;
561
- hasHistoryCache(historyKey: string): boolean;
576
+ /** Whether an intercept entry is cached for the plain `historyKey`, from any source page. */
577
+ hasInterceptCache(historyKey: string): boolean;
562
578
  /**
563
579
  * Update only the handleData (and optionally the stale / handlesPending flags)
564
580
  * of an existing cache entry. When a flag is omitted the entry's current value
@@ -602,6 +618,8 @@ export interface FetchPartialOptions {
602
618
  signal?: AbortSignal;
603
619
  /** If true, this is a stale cache revalidation request - server should force revalidators */
604
620
  staleRevalidation?: boolean;
621
+ /** Sent as `_rsc_boundary` (wire-params.ts WIRE.boundary). */
622
+ boundary?: string;
605
623
  interceptSourceUrl?: string;
606
624
  /** RSC version for cache invalidation detection */
607
625
  version?: string;
@@ -666,7 +684,8 @@ export interface ServerActionBridgeConfig {
666
684
  */
667
685
  export interface NavigationBridge {
668
686
  navigate(url: string, options?: NavigateOptionsInternal): Promise<void>;
669
- refresh(): Promise<void>;
687
+ /** `crossTab`: another tab's write; an action's error boundary stays. */
688
+ refresh(crossTab?: boolean): Promise<void>;
670
689
  /** `event`: the popstate event; a traversal the browser animated itself (hasUAVisualTransition) is not animated again. */
671
690
  handlePopstate(event?: PopStateEvent): Promise<void>;
672
691
  registerLinkInterception(): () => void;
@@ -0,0 +1,26 @@
1
+ /** The parameters of a partial request: a navigation, a prefetch or a JS server action. */
2
+ export interface PartialRequestInit {
3
+ /** A JS server action's id: written as `_rsc_action` in place of `_rsc_partial`. */
4
+ action?: string;
5
+ /** The mounted segment ids the server diffs against. */
6
+ segmentIds: readonly string[];
7
+ /**
8
+ * An empty `segmentIds`: "send" writes `_rsc_segments=` (navigation,
9
+ * action), "omit" leaves it out (prefetch). The prefetch cache keys carry
10
+ * the difference.
11
+ */
12
+ emptySegments: "send" | "omit";
13
+ stale?: boolean;
14
+ boundary?: string;
15
+ version?: string;
16
+ routerId?: string;
17
+ rewriteTarget?: string;
18
+ fill?: boolean;
19
+ }
20
+ /**
21
+ * Writes the partial-request parameters onto `url` in the one order every
22
+ * sender uses. Navigation and prefetch cache keys are built from the result
23
+ * (prefetch/cache.ts), so a navigation adopts a prefetch only while both
24
+ * write identically: browser/__tests__/wire-urls.test.ts pins the strings.
25
+ */
26
+ export declare function writePartialParams(url: URL, init: PartialRequestInit): URL;
@@ -97,8 +97,10 @@ export declare const KEY_PART_PREFIX = "key:";
97
97
  * (CacheScope.resolveKeyFrom, CacheScope.resolvePartition): the `key()`
98
98
  * results of the scope's cache() chain, outermost first, then its default
99
99
  * parts: the keyGenerator results of enclosing scopes on another store
100
- * (#974) and, for a record key under a scope without its own `key()`, its
101
- * default key (or its store's keyGenerator result, #970).
100
+ * (#974) and, for a route record key, the scope's own part: without a
101
+ * `key()` of its own, its default key (or its store's keyGenerator result,
102
+ * #970); with one, the default key's request kind (`doc`, `partial` or
103
+ * `intercept:<intercept>`, routeRecordKind in cache-scope.ts).
102
104
  *
103
105
  * Key scheme invariant (#975). A `key()` result is often request input,
104
106
  * and a record key has no route or scope discriminator (the store reads
@@ -124,9 +126,13 @@ export declare const KEY_PART_PREFIX = "key:";
124
126
  * A namespaced `key()` result starts with `key:` and holds no `|`. A
125
127
  * composed key holds a `|` and either starts with `key:` or holds no `:`.
126
128
  * Among composed keys, splitting on `|` recovers each part, a part's kind
127
- * (a `key:` prefix, or no `:` at all) and its value. Pinned by the collision
128
- * probe in src/cache/__tests__/cache-scope-chain.test.ts. A keyGenerator
129
- * result is the store's own namespace, as before.
129
+ * (a `key:` prefix, or no `:` at all) and its value. A request kind part is
130
+ * a default key's prefix without the URL after it, so it never equals a
131
+ * default key part; a keyGenerator part can equal it (`doc`) only when the
132
+ * keyGenerator returns a constant, which already gives every URL one record.
133
+ * Pinned by the collision probe in
134
+ * src/cache/__tests__/cache-scope-chain.test.ts. A keyGenerator result is
135
+ * the store's own namespace, as before.
130
136
  *
131
137
  * Shared with the testing helper `shellCacheKey`, so a test builds the
132
138
  * production partition.
@@ -14,16 +14,23 @@ import { type ExecutionStart } from "./tag-invalidation.js";
14
14
  import { type OwnedPushDelivery } from "./handle-snapshot.js";
15
15
  import type { RequestContext } from "../server/request-context.js";
16
16
  type CacheKeyFn = NonNullable<PartialCacheOptions["key"]>;
17
+ /**
18
+ * A route record that answered. `restoreHandles` is set when the lookup left
19
+ * the handle restore to its caller (lookupRouteDetailed `deferHandles`),
20
+ * which names the segments it sends (handle-snapshot.ts restoreHandles).
21
+ */
22
+ export interface CachedRouteHit {
23
+ segments: ResolvedSegment[];
24
+ shouldRevalidate: boolean;
25
+ restoreHandles?: (sentIds: ReadonlySet<string>) => Promise<void>;
26
+ }
17
27
  /**
18
28
  * Discriminated outcome of a route cache lookup — see
19
29
  * {@link CacheScope.lookupRouteDetailed} for what each status licenses.
20
30
  */
21
31
  export type CacheRouteLookupOutcome = {
22
32
  status: "hit";
23
- result: {
24
- segments: ResolvedSegment[];
25
- shouldRevalidate: boolean;
26
- };
33
+ result: CachedRouteHit;
27
34
  } | {
28
35
  status: "miss" | "bypass" | "error";
29
36
  };
@@ -187,14 +194,18 @@ export declare class CacheScope {
187
194
  * enclosing partition (#970, #974). A scope whose own config sets no
188
195
  * `key()` appends its own default key (resolveDefaultKey). Reusing the
189
196
  * outer `key()` result alone would give every route under the inner
190
- * cache() one record: a `key()` replaces the whole default key, so an
191
- * outer key that names no route (`tier:gold`) would make `/a` HIT `/b`'s
192
- * record. A scope with its own `key()` adds no default key: a `key()` is a
193
- * full override, and its store's keyGenerator does not run. With no
194
- * partition part it is resolveDefaultKey alone. With a `key()` on the
195
- * chain but no request context it rejects (keyWithoutContextError).
196
- * `partitionedPrefix` prefixes a key built from partition parts only (the
197
- * response family's `response:`).
197
+ * cache() one record: a `key()` replaces the default key's URL part, so
198
+ * an outer key that names no route (`tier:gold`) would make `/a` HIT
199
+ * `/b`'s record. A scope with its own `key()` adds no default key: its `key()`
200
+ * replaces the URL part, and its store's keyGenerator does not run. It
201
+ * appends `kind` instead (routeRecordKind): the `key()` decides which
202
+ * requests of one kind share a record, never that a document, a
203
+ * navigation and an intercept navigation do. With no partition part it is
204
+ * resolveDefaultKey alone. With a `key()` on the chain but no request
205
+ * context it rejects (keyWithoutContextError). `partitionedPrefix`
206
+ * prefixes a key built from partition parts only (the response family's
207
+ * `response:`, which passes no `kind`: a response route answers every
208
+ * request alike).
198
209
  *
199
210
  * Each `key()` result lives on the request context (`_resolvedCacheKeys`),
200
211
  * by function: it runs once per request whatever the default key
@@ -205,7 +216,7 @@ export declare class CacheScope {
205
216
  * itself: a `key()` calling `cookies()` would trip the capture guard there,
206
217
  * and a capture must store the partition its request resolved.
207
218
  */
208
- resolveKeyFrom(defaultKey: string, partitionedPrefix?: string): Promise<string>;
219
+ resolveKeyFrom(defaultKey: string, partitionedPrefix?: string, kind?: string): Promise<string>;
209
220
  /**
210
221
  * @internal The request partition of a ppr route's shell under this scope
211
222
  * (resolveShellPartition): its partition parts (resolvePartitionParts),
@@ -285,14 +296,14 @@ export declare class CacheScope {
285
296
  *
286
297
  * @param pathname - URL pathname for cache key generation
287
298
  * @param params - Route params for cache key generation
288
- * @param isIntercept - Whether this is an intercept navigation (uses different cache key)
299
+ * @param intercept - The intercept an intercept navigation selected
300
+ * (interceptRecordKey): its record has its own key
289
301
  * @param ownedPushes - What the record is to each loader's handle pushes
290
302
  * (withCacheLookup: loader-cache.ts loaderPins), called on a hit
303
+ * @param deferHandles - Restore no handle value here: the caller calls the
304
+ * hit's `restoreHandles` once it decided which segments it sends
291
305
  */
292
- lookupRoute(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: () => OwnedPushDelivery): Promise<{
293
- segments: ResolvedSegment[];
294
- shouldRevalidate: boolean;
295
- } | null>;
306
+ lookupRoute(pathname: string, params: Record<string, string>, intercept?: string, ownedPushes?: () => OwnedPushDelivery, deferHandles?: boolean): Promise<CachedRouteHit | null>;
296
307
  /**
297
308
  * @internal lookupRoute with a discriminated outcome. The PPR replay
298
309
  * composition (withCacheLookup) may substitute the seeded doc record ONLY on
@@ -311,7 +322,7 @@ export declare class CacheScope {
311
322
  * A corrupt entry that was evicted reads as `miss`: the store was consulted
312
323
  * and holds nothing servable, which is the post-eviction truth.
313
324
  */
314
- lookupRouteDetailed(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: () => OwnedPushDelivery): Promise<CacheRouteLookupOutcome>;
325
+ lookupRouteDetailed(pathname: string, params: Record<string, string>, intercept?: string, ownedPushes?: () => OwnedPushDelivery, deferHandles?: boolean): Promise<CacheRouteLookupOutcome>;
315
326
  /**
316
327
  * Record this scope's segment-DSL cache({ tags }) into the request tag union
317
328
  * synchronously, under the same gate cacheRoute() uses for a write.
@@ -341,13 +352,14 @@ export declare class CacheScope {
341
352
  * @param pathname - URL pathname for cache key generation
342
353
  * @param params - Route params for cache key generation
343
354
  * @param segments - All resolved segments to cache
344
- * @param isIntercept - Whether this is an intercept navigation (uses different cache key)
355
+ * @param intercept - The intercept an intercept navigation selected
356
+ * (interceptRecordKey): its record has its own key
345
357
  * @param start - Where the render of `segments` started: the record is not
346
358
  * written when one of its tags was invalidated since (#977,
347
359
  * predatesInvalidation). A shell capture's doc record passes none: it
348
360
  * lives only in the shell entry, which putShell gates by its own start.
349
361
  */
350
- cacheRoute(pathname: string, params: Record<string, string>, segments: ResolvedSegment[], isIntercept?: boolean, start?: ExecutionStart): Promise<void>;
362
+ cacheRoute(pathname: string, params: Record<string, string>, segments: ResolvedSegment[], intercept?: string, start?: ExecutionStart): Promise<void>;
351
363
  }
352
364
  /**
353
365
  * @internal The request partition of a ppr route's shell: with a route
@@ -392,7 +404,7 @@ export declare function createShellImplicitDocScope(marker: ShellImplicitCacheMa
392
404
  * normal partial navigation replay), substitute an implicit doc-level scope
393
405
  * so withCacheLookup/withCacheStore treat the WHOLE matched route as a
394
406
  * cache() boundary — the shell entry IS a cache() of the handler layer, with
395
- * loaders as the live carve-outs (resolveFreshLoadersAndYield).
407
+ * loaders as the live carve-outs (serveStoredSegments).
396
408
  *
397
409
  * A document HIT tail (`docTail`) always gets the implicit scope: it replays
398
410
  * the shell's own record and never consults a route-derived scope, whose
@@ -38,13 +38,13 @@ export declare const CACHE_REVALIDATING_AT_HEADER = "x-edge-cache-revalidating-a
38
38
  * re-arm re-puts the full window and restarts CF's retention clock, pinning a
39
39
  * perpetually-stale entry (one whose background revalidation keeps failing) past
40
40
  * its intended hard-expiry indefinitely. Mirrors the KVSegmentEnvelope `e`
41
- * field and the remaining-ttl math in promoteSegmentToL1/promoteItemToL1.
41
+ * field and the remaining-ttl math in promoteToL1.
42
42
  * @internal
43
43
  */
44
44
  export declare const CACHE_EXPIRES_AT_HEADER = "x-edge-cache-expires-at";
45
45
  /**
46
46
  * Header stashing the route author's original Cache-Control on L1 document
47
- * entries. putResponse/promoteResponseToL1 overwrite Cache-Control with a long
47
+ * entries. putResponse/promoteToL1 overwrite Cache-Control with a long
48
48
  * `max-age` so the CF Cache API retains the entry across the whole SWR window;
49
49
  * getResponse restores this original value before serving so the client and any
50
50
  * upstream CDN see the author's intended directive, not the internal edge TTL.