@rangojs/router 0.0.0-experimental.9 → 0.0.0-experimental.98d9a51b

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 (299) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1532 -155
  4. package/dist/vite/index.js +4444 -2235
  5. package/package.json +69 -62
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +85 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +6 -4
  13. package/skills/hooks/SKILL.md +333 -71
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +74 -15
  18. package/skills/loader/SKILL.md +388 -38
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +15 -11
  21. package/skills/parallel/SKILL.md +78 -1
  22. package/skills/prerender/SKILL.md +405 -45
  23. package/skills/rango/SKILL.md +85 -21
  24. package/skills/response-routes/SKILL.md +144 -91
  25. package/skills/route/SKILL.md +226 -14
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/theme/SKILL.md +9 -8
  28. package/skills/typesafety/SKILL.md +316 -87
  29. package/skills/use-cache/SKILL.md +324 -0
  30. package/src/__internal.ts +102 -4
  31. package/src/bin/rango.ts +312 -15
  32. package/src/browser/action-coordinator.ts +97 -0
  33. package/src/browser/action-response-classifier.ts +99 -0
  34. package/src/browser/event-controller.ts +87 -64
  35. package/src/browser/history-state.ts +80 -0
  36. package/src/browser/intercept-utils.ts +52 -0
  37. package/src/browser/link-interceptor.ts +24 -4
  38. package/src/browser/logging.ts +55 -0
  39. package/src/browser/merge-segment-loaders.ts +20 -12
  40. package/src/browser/navigation-bridge.ts +282 -557
  41. package/src/browser/navigation-client.ts +169 -73
  42. package/src/browser/navigation-store.ts +33 -50
  43. package/src/browser/navigation-transaction.ts +297 -0
  44. package/src/browser/network-error-handler.ts +61 -0
  45. package/src/browser/partial-update.ts +292 -310
  46. package/src/browser/prefetch/cache.ts +206 -0
  47. package/src/browser/prefetch/fetch.ts +145 -0
  48. package/src/browser/prefetch/observer.ts +65 -0
  49. package/src/browser/prefetch/policy.ts +48 -0
  50. package/src/browser/prefetch/queue.ts +128 -0
  51. package/src/browser/rango-state.ts +112 -0
  52. package/src/browser/react/Link.tsx +190 -70
  53. package/src/browser/react/NavigationProvider.tsx +78 -11
  54. package/src/browser/react/context.ts +6 -0
  55. package/src/browser/react/filter-segment-order.ts +11 -0
  56. package/src/browser/react/index.ts +12 -12
  57. package/src/browser/react/location-state-shared.ts +95 -53
  58. package/src/browser/react/location-state.ts +60 -15
  59. package/src/browser/react/mount-context.ts +6 -1
  60. package/src/browser/react/nonce-context.ts +23 -0
  61. package/src/browser/react/shallow-equal.ts +27 -0
  62. package/src/browser/react/use-action.ts +29 -51
  63. package/src/browser/react/use-client-cache.ts +5 -3
  64. package/src/browser/react/use-handle.ts +29 -70
  65. package/src/browser/react/use-link-status.ts +6 -5
  66. package/src/browser/react/use-navigation.ts +22 -63
  67. package/src/browser/react/use-params.ts +65 -0
  68. package/src/browser/react/use-pathname.ts +47 -0
  69. package/src/browser/react/use-router.ts +63 -0
  70. package/src/browser/react/use-search-params.ts +56 -0
  71. package/src/browser/react/use-segments.ts +80 -97
  72. package/src/browser/response-adapter.ts +73 -0
  73. package/src/browser/rsc-router.tsx +106 -27
  74. package/src/browser/scroll-restoration.ts +117 -44
  75. package/src/browser/segment-reconciler.ts +216 -0
  76. package/src/browser/segment-structure-assert.ts +16 -0
  77. package/src/browser/server-action-bridge.ts +504 -599
  78. package/src/browser/shallow.ts +6 -1
  79. package/src/browser/types.ts +116 -47
  80. package/src/browser/validate-redirect-origin.ts +29 -0
  81. package/src/build/generate-manifest.ts +82 -21
  82. package/src/build/generate-route-types.ts +36 -752
  83. package/src/build/index.ts +6 -5
  84. package/src/build/route-trie.ts +39 -13
  85. package/src/build/route-types/ast-helpers.ts +25 -0
  86. package/src/build/route-types/ast-route-extraction.ts +98 -0
  87. package/src/build/route-types/codegen.ts +102 -0
  88. package/src/build/route-types/include-resolution.ts +411 -0
  89. package/src/build/route-types/param-extraction.ts +48 -0
  90. package/src/build/route-types/per-module-writer.ts +128 -0
  91. package/src/build/route-types/router-processing.ts +479 -0
  92. package/src/build/route-types/scan-filter.ts +78 -0
  93. package/src/build/runtime-discovery.ts +231 -0
  94. package/src/cache/background-task.ts +34 -0
  95. package/src/cache/cache-key-utils.ts +44 -0
  96. package/src/cache/cache-policy.ts +125 -0
  97. package/src/cache/cache-runtime.ts +338 -0
  98. package/src/cache/cache-scope.ts +122 -303
  99. package/src/cache/cf/cf-cache-store.ts +571 -17
  100. package/src/cache/cf/index.ts +13 -3
  101. package/src/cache/document-cache.ts +101 -72
  102. package/src/cache/handle-capture.ts +81 -0
  103. package/src/cache/handle-snapshot.ts +41 -0
  104. package/src/cache/index.ts +1 -15
  105. package/src/cache/memory-segment-store.ts +191 -13
  106. package/src/cache/profile-registry.ts +73 -0
  107. package/src/cache/read-through-swr.ts +134 -0
  108. package/src/cache/segment-codec.ts +256 -0
  109. package/src/cache/taint.ts +98 -0
  110. package/src/cache/types.ts +72 -122
  111. package/src/client.rsc.tsx +3 -1
  112. package/src/client.tsx +84 -126
  113. package/src/component-utils.ts +4 -4
  114. package/src/components/DefaultDocument.tsx +5 -1
  115. package/src/context-var.ts +86 -0
  116. package/src/debug.ts +17 -7
  117. package/src/errors.ts +77 -7
  118. package/src/handle.ts +15 -10
  119. package/src/handles/MetaTags.tsx +73 -20
  120. package/src/handles/breadcrumbs.ts +66 -0
  121. package/src/handles/index.ts +1 -0
  122. package/src/handles/meta.ts +30 -13
  123. package/src/host/cookie-handler.ts +21 -15
  124. package/src/host/errors.ts +8 -8
  125. package/src/host/index.ts +4 -7
  126. package/src/host/pattern-matcher.ts +27 -27
  127. package/src/host/router.ts +61 -39
  128. package/src/host/testing.ts +8 -8
  129. package/src/host/types.ts +15 -7
  130. package/src/host/utils.ts +1 -1
  131. package/src/href-client.ts +65 -45
  132. package/src/index.rsc.ts +133 -21
  133. package/src/index.ts +164 -52
  134. package/src/internal-debug.ts +11 -0
  135. package/src/loader.rsc.ts +25 -143
  136. package/src/loader.ts +27 -10
  137. package/src/network-error-thrower.tsx +3 -1
  138. package/src/outlet-provider.tsx +45 -0
  139. package/src/prerender/param-hash.ts +4 -2
  140. package/src/prerender/store.ts +158 -13
  141. package/src/prerender.ts +333 -26
  142. package/src/reverse.ts +184 -121
  143. package/src/root-error-boundary.tsx +41 -29
  144. package/src/route-content-wrapper.tsx +7 -4
  145. package/src/route-definition/dsl-helpers.ts +934 -0
  146. package/src/route-definition/helper-factories.ts +200 -0
  147. package/src/route-definition/helpers-types.ts +430 -0
  148. package/src/route-definition/index.ts +52 -0
  149. package/src/route-definition/redirect.ts +93 -0
  150. package/src/route-definition.ts +1 -1431
  151. package/src/route-map-builder.ts +162 -123
  152. package/src/route-name.ts +53 -0
  153. package/src/route-types.ts +48 -9
  154. package/src/router/content-negotiation.ts +116 -0
  155. package/src/router/debug-manifest.ts +72 -0
  156. package/src/router/error-handling.ts +9 -9
  157. package/src/router/find-match.ts +160 -0
  158. package/src/router/handler-context.ts +374 -81
  159. package/src/router/intercept-resolution.ts +26 -16
  160. package/src/router/lazy-includes.ts +236 -0
  161. package/src/router/loader-resolution.ts +215 -122
  162. package/src/router/logging.ts +251 -0
  163. package/src/router/manifest.ts +85 -32
  164. package/src/router/match-api.ts +118 -119
  165. package/src/router/match-context.ts +4 -2
  166. package/src/router/match-handlers.ts +440 -0
  167. package/src/router/match-middleware/background-revalidation.ts +80 -93
  168. package/src/router/match-middleware/cache-lookup.ts +336 -84
  169. package/src/router/match-middleware/cache-store.ts +43 -24
  170. package/src/router/match-middleware/intercept-resolution.ts +45 -20
  171. package/src/router/match-middleware/segment-resolution.ts +17 -8
  172. package/src/router/match-pipelines.ts +10 -45
  173. package/src/router/match-result.ts +34 -28
  174. package/src/router/metrics.ts +235 -15
  175. package/src/router/middleware-cookies.ts +55 -0
  176. package/src/router/middleware-types.ts +222 -0
  177. package/src/router/middleware.ts +327 -369
  178. package/src/router/pattern-matching.ts +197 -41
  179. package/src/router/prerender-match.ts +402 -0
  180. package/src/router/preview-match.ts +170 -0
  181. package/src/router/revalidation.ts +137 -38
  182. package/src/router/router-context.ts +40 -21
  183. package/src/router/router-interfaces.ts +452 -0
  184. package/src/router/router-options.ts +592 -0
  185. package/src/router/router-registry.ts +24 -0
  186. package/src/router/segment-resolution/fresh.ts +570 -0
  187. package/src/router/segment-resolution/helpers.ts +263 -0
  188. package/src/router/segment-resolution/loader-cache.ts +198 -0
  189. package/src/router/segment-resolution/revalidation.ts +1242 -0
  190. package/src/router/segment-resolution/static-store.ts +67 -0
  191. package/src/router/segment-resolution.ts +21 -1315
  192. package/src/router/segment-wrappers.ts +291 -0
  193. package/src/router/telemetry-otel.ts +299 -0
  194. package/src/router/telemetry.ts +300 -0
  195. package/src/router/timeout.ts +148 -0
  196. package/src/router/trie-matching.ts +96 -29
  197. package/src/router/types.ts +16 -9
  198. package/src/router.ts +595 -1984
  199. package/src/rsc/handler-context.ts +45 -0
  200. package/src/rsc/handler.ts +680 -1027
  201. package/src/rsc/helpers.ts +140 -6
  202. package/src/rsc/index.ts +0 -20
  203. package/src/rsc/loader-fetch.ts +209 -0
  204. package/src/rsc/manifest-init.ts +86 -0
  205. package/src/rsc/nonce.ts +14 -0
  206. package/src/rsc/origin-guard.ts +141 -0
  207. package/src/rsc/progressive-enhancement.ts +379 -0
  208. package/src/rsc/response-error.ts +37 -0
  209. package/src/rsc/response-route-handler.ts +347 -0
  210. package/src/rsc/rsc-rendering.ts +237 -0
  211. package/src/rsc/runtime-warnings.ts +42 -0
  212. package/src/rsc/server-action.ts +348 -0
  213. package/src/rsc/ssr-setup.ts +128 -0
  214. package/src/rsc/types.ts +38 -11
  215. package/src/search-params.ts +230 -0
  216. package/src/segment-system.tsx +25 -13
  217. package/src/server/context.ts +173 -48
  218. package/src/server/cookie-store.ts +190 -0
  219. package/src/server/fetchable-loader-store.ts +37 -0
  220. package/src/server/handle-store.ts +94 -15
  221. package/src/server/loader-registry.ts +15 -56
  222. package/src/server/request-context.ts +439 -73
  223. package/src/server.ts +35 -155
  224. package/src/ssr/index.tsx +100 -31
  225. package/src/static-handler.ts +114 -0
  226. package/src/theme/ThemeProvider.tsx +21 -15
  227. package/src/theme/ThemeScript.tsx +5 -5
  228. package/src/theme/constants.ts +5 -2
  229. package/src/theme/index.ts +4 -14
  230. package/src/theme/theme-context.ts +4 -30
  231. package/src/theme/theme-script.ts +21 -18
  232. package/src/types/boundaries.ts +158 -0
  233. package/src/types/cache-types.ts +198 -0
  234. package/src/types/error-types.ts +192 -0
  235. package/src/types/global-namespace.ts +100 -0
  236. package/src/types/handler-context.ts +687 -0
  237. package/src/types/index.ts +88 -0
  238. package/src/types/loader-types.ts +183 -0
  239. package/src/types/route-config.ts +170 -0
  240. package/src/types/route-entry.ts +109 -0
  241. package/src/types/segments.ts +148 -0
  242. package/src/types.ts +1 -1757
  243. package/src/urls/include-helper.ts +197 -0
  244. package/src/urls/index.ts +53 -0
  245. package/src/urls/path-helper-types.ts +339 -0
  246. package/src/urls/path-helper.ts +329 -0
  247. package/src/urls/pattern-types.ts +95 -0
  248. package/src/urls/response-types.ts +106 -0
  249. package/src/urls/type-extraction.ts +372 -0
  250. package/src/urls/urls-function.ts +98 -0
  251. package/src/urls.ts +1 -1282
  252. package/src/use-loader.tsx +85 -77
  253. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  254. package/src/vite/discovery/discover-routers.ts +344 -0
  255. package/src/vite/discovery/prerender-collection.ts +385 -0
  256. package/src/vite/discovery/route-types-writer.ts +258 -0
  257. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  258. package/src/vite/discovery/state.ts +108 -0
  259. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  260. package/src/vite/index.ts +11 -1963
  261. package/src/vite/plugin-types.ts +48 -0
  262. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  263. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  264. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  265. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  266. package/src/vite/plugins/expose-id-utils.ts +287 -0
  267. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  268. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  269. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  270. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  271. package/src/vite/plugins/expose-ids/types.ts +45 -0
  272. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  273. package/src/vite/plugins/refresh-cmd.ts +65 -0
  274. package/src/vite/plugins/use-cache-transform.ts +323 -0
  275. package/src/vite/plugins/version-injector.ts +83 -0
  276. package/src/vite/plugins/version-plugin.ts +254 -0
  277. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +27 -14
  278. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  279. package/src/vite/rango.ts +445 -0
  280. package/src/vite/router-discovery.ts +777 -0
  281. package/src/vite/utils/ast-handler-extract.ts +517 -0
  282. package/src/vite/utils/banner.ts +36 -0
  283. package/src/vite/utils/bundle-analysis.ts +137 -0
  284. package/src/vite/utils/manifest-utils.ts +70 -0
  285. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  286. package/src/vite/utils/prerender-utils.ts +189 -0
  287. package/src/vite/utils/shared-utils.ts +169 -0
  288. package/CLAUDE.md +0 -43
  289. package/src/browser/lru-cache.ts +0 -69
  290. package/src/browser/request-controller.ts +0 -164
  291. package/src/cache/memory-store.ts +0 -253
  292. package/src/href-context.ts +0 -33
  293. package/src/router.gen.ts +0 -6
  294. package/src/urls.gen.ts +0 -8
  295. package/src/vite/expose-handle-id.ts +0 -209
  296. package/src/vite/expose-loader-id.ts +0 -426
  297. package/src/vite/expose-location-state-id.ts +0 -177
  298. package/src/vite/expose-prerender-handler-id.ts +0 -429
  299. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -75,14 +75,14 @@ export interface SegmentCacheStore<TEnv = unknown> {
75
75
  * @example Using cookies for locale
76
76
  * ```typescript
77
77
  * keyGenerator: (ctx, defaultKey) => {
78
- * const locale = ctx.cookie('locale') || 'en';
78
+ * const locale = cookies().get('locale')?.value || 'en';
79
79
  * return `${locale}:${defaultKey}`;
80
80
  * }
81
81
  * ```
82
82
  */
83
83
  readonly keyGenerator?: (
84
84
  ctx: RequestContext<TEnv>,
85
- defaultKey: string
85
+ defaultKey: string,
86
86
  ) => string | Promise<string>;
87
87
 
88
88
  /**
@@ -98,7 +98,12 @@ export interface SegmentCacheStore<TEnv = unknown> {
98
98
  * @param ttl - Time-to-live in seconds
99
99
  * @param swr - Optional stale-while-revalidate window in seconds
100
100
  */
101
- set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise<void>;
101
+ set(
102
+ key: string,
103
+ data: CachedEntryData,
104
+ ttl: number,
105
+ swr?: number,
106
+ ): Promise<void>;
102
107
 
103
108
  /**
104
109
  * Delete a cached entry
@@ -121,7 +126,9 @@ export interface SegmentCacheStore<TEnv = unknown> {
121
126
  * Get a cached Response by key.
122
127
  * Returns the response and whether it should be revalidated (SWR).
123
128
  */
124
- getResponse?(key: string): Promise<{ response: Response; shouldRevalidate: boolean } | null>;
129
+ getResponse?(
130
+ key: string,
131
+ ): Promise<{ response: Response; shouldRevalidate: boolean } | null>;
125
132
 
126
133
  /**
127
134
  * Store a Response with TTL and optional SWR window.
@@ -130,7 +137,62 @@ export interface SegmentCacheStore<TEnv = unknown> {
130
137
  * @param ttl - Time-to-live in seconds
131
138
  * @param swr - Optional stale-while-revalidate window in seconds
132
139
  */
133
- putResponse?(key: string, response: Response, ttl: number, swr?: number): Promise<void>;
140
+ putResponse?(
141
+ key: string,
142
+ response: Response,
143
+ ttl: number,
144
+ swr?: number,
145
+ ): Promise<void>;
146
+
147
+ // ============================================================================
148
+ // Function Cache Methods (optional, for "use cache" directive)
149
+ // ============================================================================
150
+ // These methods cache individual function/component return values.
151
+ // Stores that support "use cache" should implement these methods.
152
+
153
+ /**
154
+ * Get a cached function result by key.
155
+ * Returns the serialized value, optional handle data, and staleness flag.
156
+ */
157
+ getItem?(key: string): Promise<CacheItemResult | null>;
158
+
159
+ /**
160
+ * Store a function result with TTL and optional SWR window.
161
+ * @param key - Cache key (format: use-cache:{functionId}:{serializedArgs})
162
+ * @param value - RSC-serialized return value
163
+ * @param options - TTL, SWR, handle data, and tags
164
+ */
165
+ setItem?(
166
+ key: string,
167
+ value: string,
168
+ options?: CacheItemOptions,
169
+ ): Promise<void>;
170
+ }
171
+
172
+ /**
173
+ * Result from getItem() for function-level caching ("use cache").
174
+ */
175
+ export interface CacheItemResult {
176
+ /** RSC-serialized return value */
177
+ value: string;
178
+ /** Handle data captured during execution (breadcrumbs, metadata, etc.) */
179
+ handles?: Record<string, SegmentHandleData>;
180
+ /** Whether the entry is stale and should be revalidated */
181
+ shouldRevalidate: boolean;
182
+ }
183
+
184
+ /**
185
+ * Options for setItem() for function-level caching ("use cache").
186
+ */
187
+ export interface CacheItemOptions {
188
+ /** Handle data to store alongside the value */
189
+ handles?: Record<string, SegmentHandleData>;
190
+ /** Time-to-live in seconds */
191
+ ttl?: number;
192
+ /** Stale-while-revalidate window in seconds */
193
+ swr?: number;
194
+ /** Cache tags for invalidation */
195
+ tags?: string[];
134
196
  }
135
197
 
136
198
  /**
@@ -151,7 +213,10 @@ export interface SerializedSegmentData {
151
213
  /** RSC-encoded loaderDataPromise (if present) */
152
214
  encodedLoaderDataPromise?: string;
153
215
  /** Segment metadata (everything except component, layout, loading, and loader data) */
154
- metadata: Omit<ResolvedSegment, "component" | "layout" | "loading" | "loaderData" | "loaderDataPromise">;
216
+ metadata: Omit<
217
+ ResolvedSegment,
218
+ "component" | "layout" | "loading" | "loaderData" | "loaderDataPromise"
219
+ >;
155
220
  }
156
221
 
157
222
  /**
@@ -234,7 +299,6 @@ export interface CachedEntryResult {
234
299
  handles: Record<string, SegmentHandleData>;
235
300
  }
236
301
 
237
-
238
302
  /**
239
303
  * Segment cache provider interface
240
304
  *
@@ -262,7 +326,7 @@ export interface SegmentCacheProvider {
262
326
  restore(
263
327
  cacheKey: string,
264
328
  params: Record<string, string>,
265
- loaderPromises: Map<string, Promise<any>>
329
+ loaderPromises: Map<string, Promise<any>>,
266
330
  ): Promise<[ResolvedSegment[], string[]] | null>;
267
331
 
268
332
  /**
@@ -276,117 +340,3 @@ export interface SegmentCacheProvider {
276
340
  */
277
341
  cacheEntry(cacheKey: string, segments: ResolvedSegment[]): void;
278
342
  }
279
-
280
- // ============================================================================
281
- // Generic Cache Store (for future extensibility)
282
- // ============================================================================
283
- // These types support a general-purpose cache interface that can be used
284
- // for caching arbitrary values (responses, streams, objects). Currently,
285
- // the segment caching system uses SegmentCacheStore directly, but these
286
- // types enable future use cases like response caching or data caching.
287
-
288
- /**
289
- * Supported cache value types for the generic CacheStore interface.
290
- * @internal Reserved for future extensibility
291
- */
292
- export type CacheValue =
293
- | ReadableStream<Uint8Array>
294
- | Response
295
- | ArrayBuffer
296
- | string
297
- | unknown[] // JSON-serializable array
298
- | Record<string, unknown>; // JSON-serializable object
299
-
300
- /**
301
- * Cache entry returned by match().
302
- * @internal Reserved for future extensibility
303
- */
304
- export interface CacheEntry<T = CacheValue> {
305
- /** The cached value */
306
- value: T;
307
- /** Optional metadata stored with the entry */
308
- metadata?: CacheMetadata;
309
- }
310
-
311
- /**
312
- * Original value type for reconstruction.
313
- * @internal Reserved for future extensibility
314
- */
315
- export type CacheValueType =
316
- | "stream"
317
- | "response"
318
- | "arraybuffer"
319
- | "string"
320
- | "object";
321
-
322
- /**
323
- * Metadata associated with a cache entry.
324
- * @internal Reserved for future extensibility
325
- */
326
- export interface CacheMetadata {
327
- /** Timestamp when entry expires (ms since epoch) */
328
- expiresAt?: number;
329
- /** Tags for bulk invalidation */
330
- tags?: string[];
331
- /** Original value type for reconstruction on read */
332
- valueType?: CacheValueType;
333
- /** Response headers (preserved when caching Response) */
334
- responseHeaders?: Record<string, string>;
335
- /** Response status (preserved when caching Response) */
336
- responseStatus?: number;
337
- /** Custom metadata */
338
- [key: string]: unknown;
339
- }
340
-
341
- /**
342
- * Options for put().
343
- * @internal Reserved for future extensibility
344
- */
345
- export interface CachePutOptions {
346
- /** Time-to-live in seconds */
347
- ttl?: number;
348
- /** Metadata to store with entry */
349
- metadata?: Omit<CacheMetadata, "expiresAt">;
350
- }
351
-
352
- /**
353
- * Generic cache store interface for arbitrary value types.
354
- *
355
- * This interface is designed for future extensibility to support caching
356
- * responses, streams, and other values. Currently, segment caching uses
357
- * the SegmentCacheStore interface directly.
358
- *
359
- * Implementations must handle:
360
- * - Stream values (clone before storing, streams can only be read once)
361
- * - Promise values (await before storing)
362
- * - Expiration/TTL
363
- *
364
- * @internal Reserved for future extensibility
365
- */
366
- export interface CacheStore {
367
- /**
368
- * Retrieve a cached entry by key.
369
- * @param key - Cache key
370
- * @returns The cached entry or undefined if not found/expired
371
- */
372
- match<T = CacheValue>(key: string): Promise<CacheEntry<T> | undefined>;
373
-
374
- /**
375
- * Store a value in the cache.
376
- * @param key - Cache key
377
- * @param value - Value to cache (stream, response, string, object, etc.)
378
- * @param options - TTL, metadata, etc.
379
- */
380
- put<T extends CacheValue>(
381
- key: string,
382
- value: T,
383
- options?: CachePutOptions
384
- ): Promise<void>;
385
-
386
- /**
387
- * Delete a cached entry.
388
- * @param key - Cache key
389
- * @returns true if entry was deleted, false if not found
390
- */
391
- delete(key: string): Promise<boolean>;
392
- }
@@ -17,7 +17,6 @@ export {
17
17
  OutletProvider,
18
18
  useOutlet,
19
19
  useLoader,
20
- useLoaderData,
21
20
  ErrorBoundary,
22
21
  type ErrorBoundaryProps,
23
22
  } from "./client.js";
@@ -64,6 +63,8 @@ export { Meta } from "./handles/meta.js";
64
63
  // MetaTags is a "use client" component that can be imported from RSC
65
64
  export { MetaTags } from "./handles/MetaTags.js";
66
65
  export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
66
+ // Breadcrumbs handle works in RSC context
67
+ export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
67
68
 
68
69
  // Location state - createLocationState works in RSC (just creates definition)
69
70
  // useLocationState is NOT exported here as it uses client hooks
@@ -71,6 +72,7 @@ export {
71
72
  createLocationState,
72
73
  type LocationStateDefinition,
73
74
  type LocationStateEntry,
75
+ type LocationStateOptions,
74
76
  } from "./browser/react/location-state-shared.js";
75
77
 
76
78
  // Re-export useHref - it's a "use client" hook
package/src/client.tsx CHANGED
@@ -20,6 +20,8 @@ import {
20
20
  RouteContentWrapper,
21
21
  LoaderBoundary,
22
22
  } from "./route-content-wrapper.js";
23
+ import { OutletProvider } from "./outlet-provider.js";
24
+ import { MountContextProvider } from "./browser/react/mount-context.js";
23
25
 
24
26
  /**
25
27
  * Outlet component - renders child content in layouts
@@ -86,6 +88,8 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
86
88
  content = segment.component ?? null;
87
89
  }
88
90
 
91
+ let result: ReactNode;
92
+
89
93
  // If segment has a layout, wrap appropriately
90
94
  if (segment.layout) {
91
95
  // Check if this segment has loaders that need streaming
@@ -105,25 +109,23 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
105
109
  </LoaderBoundary>
106
110
  );
107
111
 
108
- return (
112
+ result = (
109
113
  <OutletProvider content={loaderAwareContent} segment={segment}>
110
114
  {segment.layout}
111
115
  </OutletProvider>
112
116
  );
117
+ } else {
118
+ // No loaders - wrap in OutletProvider so layout can use <Outlet />
119
+ result = (
120
+ <OutletProvider content={content} segment={segment}>
121
+ {segment.layout}
122
+ </OutletProvider>
123
+ );
113
124
  }
114
-
115
- // No loaders - wrap in OutletProvider so layout can use <Outlet />
116
- return (
117
- <OutletProvider content={content} segment={segment}>
118
- {segment.layout}
119
- </OutletProvider>
120
- );
121
- }
122
-
123
- // No layout but has loaders - wrap content with LoaderBoundary for useLoader context
124
- // This is common for intercept routes that use useLoader without a custom layout
125
- if (segment.loaderDataPromise && segment.loaderIds) {
126
- return (
125
+ } else if (segment.loaderDataPromise && segment.loaderIds) {
126
+ // No layout but has loaders - wrap content with LoaderBoundary for useLoader context
127
+ // This is common for intercept routes that use useLoader without a custom layout
128
+ result = (
127
129
  <LoaderBoundary
128
130
  loaderDataPromise={segment.loaderDataPromise}
129
131
  loaderIds={segment.loaderIds}
@@ -135,9 +137,20 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
135
137
  {content}
136
138
  </LoaderBoundary>
137
139
  );
140
+ } else {
141
+ result = content;
142
+ }
143
+
144
+ // Wrap with MountContextProvider for include() scoped parallel/intercept slots
145
+ if (segment.mountPath) {
146
+ return (
147
+ <MountContextProvider value={segment.mountPath}>
148
+ {result}
149
+ </MountContextProvider>
150
+ );
138
151
  }
139
152
 
140
- return content;
153
+ return result;
141
154
  }
142
155
 
143
156
  // Default: render child content
@@ -201,6 +214,8 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
201
214
  content = segment.component ?? null;
202
215
  }
203
216
 
217
+ let result: ReactNode;
218
+
204
219
  // If segment has a layout, wrap appropriately
205
220
  if (segment.layout) {
206
221
  // Check if this segment has loaders that need streaming
@@ -219,25 +234,23 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
219
234
  </LoaderBoundary>
220
235
  );
221
236
 
222
- return (
237
+ result = (
223
238
  <OutletProvider content={loaderAwareContent} segment={segment}>
224
239
  {segment.layout}
225
240
  </OutletProvider>
226
241
  );
242
+ } else {
243
+ // No loaders - wrap in OutletProvider so layout can use <Outlet />
244
+ result = (
245
+ <OutletProvider content={content} segment={segment}>
246
+ {segment.layout}
247
+ </OutletProvider>
248
+ );
227
249
  }
228
-
229
- // No loaders - wrap in OutletProvider so layout can use <Outlet />
230
- return (
231
- <OutletProvider content={content} segment={segment}>
232
- {segment.layout}
233
- </OutletProvider>
234
- );
235
- }
236
-
237
- // No layout but has loaders - wrap content with LoaderBoundary for useLoader context
238
- // This is common for intercept routes that use useLoader without a custom layout
239
- if (segment.loaderDataPromise && segment.loaderIds) {
240
- return (
250
+ } else if (segment.loaderDataPromise && segment.loaderIds) {
251
+ // No layout but has loaders - wrap content with LoaderBoundary for useLoader context
252
+ // This is common for intercept routes that use useLoader without a custom layout
253
+ result = (
241
254
  <LoaderBoundary
242
255
  loaderDataPromise={segment.loaderDataPromise}
243
256
  loaderIds={segment.loaderIds}
@@ -249,51 +262,28 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
249
262
  {content}
250
263
  </LoaderBoundary>
251
264
  );
265
+ } else {
266
+ result = content;
252
267
  }
253
268
 
254
- return content;
255
- }
269
+ // Wrap with MountContextProvider for include() scoped parallel/intercept slots
270
+ if (segment.mountPath) {
271
+ return (
272
+ <MountContextProvider value={segment.mountPath}>
273
+ {result}
274
+ </MountContextProvider>
275
+ );
276
+ }
256
277
 
257
- /**
258
- * Provider for outlet content - used internally by renderSegments
259
- *
260
- * Stores a reference to parent context so useLoader can walk up the chain
261
- * to find loader data from parent layouts. If this segment defines a loading
262
- * component, Outlet will wrap content with Suspense using that as fallback.
263
- */
264
- export function OutletProvider({
265
- content,
266
- parallel,
267
- segment,
268
- loaderData,
269
- children,
270
- }: {
271
- content: ReactNode;
272
- parallel?: ResolvedSegment[];
273
- segment?: ResolvedSegment;
274
- loaderData?: Record<string, any>;
275
- children: ReactNode;
276
- }): ReactNode {
277
- // Get parent context to enable walking up the chain for loader lookups
278
- const parentContext = useContext(OutletContext);
279
-
280
- const value = useMemo(
281
- () => ({
282
- content,
283
- parallel,
284
- segment,
285
- loaderData,
286
- parent: parentContext,
287
- loading: segment?.loading,
288
- }),
289
- [content, parallel, segment, loaderData, parentContext]
290
- );
291
-
292
- return (
293
- <OutletContext.Provider value={value}>{children}</OutletContext.Provider>
294
- );
278
+ return result;
295
279
  }
296
280
 
281
+ // OutletProvider is defined in outlet-provider.tsx to break a circular
282
+ // dependency between client.tsx and route-content-wrapper.tsx.
283
+ // Imported at the top of this file for local use in Outlet/ParallelOutlet,
284
+ // and re-exported here for backwards compatibility.
285
+ export { OutletProvider };
286
+
297
287
  /**
298
288
  * Hook to access outlet content programmatically
299
289
  *
@@ -323,52 +313,6 @@ export {
323
313
  type UseLoaderOptions,
324
314
  } from "./use-loader.js";
325
315
 
326
- /**
327
- * Hook to access all loader data in the current context
328
- *
329
- * Returns a record of all loader data available in the current outlet context
330
- * and all parent contexts. Useful for debugging or when you need access to
331
- * multiple loaders.
332
- *
333
- * @returns Record of loader name to data, or empty object if no loaders
334
- *
335
- * @example
336
- * ```tsx
337
- * "use client";
338
- * import { useLoaderData } from "rsc-router/client";
339
- *
340
- * export function DebugPanel() {
341
- * const loaderData = useLoaderData();
342
- * return <pre>{JSON.stringify(loaderData, null, 2)}</pre>;
343
- * }
344
- * ```
345
- */
346
- export function useLoaderData(): Record<string, any> {
347
- const context = useContext(OutletContext);
348
-
349
- // Collect all loader data from the context chain
350
- // Child loaders override parent loaders with the same name
351
- const result: Record<string, any> = {};
352
- const stack: OutletContextValue[] = [];
353
-
354
- // Build stack from current to root
355
- let current: OutletContextValue | null | undefined = context;
356
- while (current) {
357
- stack.push(current);
358
- current = current.parent;
359
- }
360
-
361
- // Apply from root to current (so children override parents)
362
- for (let i = stack.length - 1; i >= 0; i--) {
363
- const ctx = stack[i];
364
- if (ctx.loaderData) {
365
- Object.assign(result, ctx.loaderData);
366
- }
367
- }
368
-
369
- return result;
370
- }
371
-
372
316
  /**
373
317
  * Client-safe createLoader factory
374
318
  *
@@ -398,13 +342,13 @@ export function useLoaderData(): Record<string, any> {
398
342
  */
399
343
  // Overload 1: With function only (not fetchable)
400
344
  export function createLoader<T>(
401
- fn: LoaderFn<T, Record<string, string | undefined>, any>
345
+ fn: LoaderFn<T, Record<string, string | undefined>, any>,
402
346
  ): LoaderDefinition<Awaited<T>, Record<string, string | undefined>>;
403
347
 
404
348
  // Overload 2: With function and fetchable flag
405
349
  export function createLoader<T>(
406
350
  fn: LoaderFn<T, Record<string, string | undefined>, any>,
407
- fetchable: true
351
+ fetchable: true,
408
352
  ): LoaderDefinition<Awaited<T>, Record<string, string | undefined>>;
409
353
 
410
354
  // Implementation - function is ignored at runtime on client
@@ -412,7 +356,7 @@ export function createLoader<T>(
412
356
  export function createLoader(
413
357
  _fn: LoaderFn<any, Record<string, string | undefined>, any>,
414
358
  _fetchable?: true,
415
- __injectedId?: string
359
+ __injectedId?: string,
416
360
  ): LoaderDefinition<any, Record<string, string | undefined>> {
417
361
  return {
418
362
  __brand: "loader",
@@ -534,11 +478,16 @@ export class ErrorBoundary extends Component<
534
478
  // ============================================================================
535
479
 
536
480
  // Navigation hooks
537
- export {
538
- useNavigation,
539
- type NavigationMethods,
540
- type NavigationValue,
541
- } from "./browser/react/use-navigation.js";
481
+ export { useNavigation } from "./browser/react/use-navigation.js";
482
+ export { useRouter } from "./browser/react/use-router.js";
483
+ export { usePathname } from "./browser/react/use-pathname.js";
484
+ export { useSearchParams } from "./browser/react/use-search-params.js";
485
+ export { useParams } from "./browser/react/use-params.js";
486
+ export type {
487
+ RouterInstance,
488
+ RouterNavigateOptions,
489
+ ReadonlyURLSearchParams,
490
+ } from "./browser/types.js";
542
491
 
543
492
  // Action state tracking hook
544
493
  export {
@@ -595,6 +544,7 @@ export { useHandle } from "./browser/react/use-handle.js";
595
544
  export { Meta } from "./handles/meta.js";
596
545
  export { MetaTags } from "./handles/MetaTags.js";
597
546
  export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
547
+ export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
598
548
 
599
549
  // Location state - type-safe navigation state
600
550
  export {
@@ -602,10 +552,16 @@ export {
602
552
  useLocationState,
603
553
  type LocationStateDefinition,
604
554
  type LocationStateEntry,
555
+ type LocationStateOptions,
605
556
  } from "./browser/react/location-state.js";
606
557
 
607
558
  // Type-safe href for client-side path validation
608
- export { href, type ValidPaths, type PatternToPath, type PathResponse } from "./href-client.js";
559
+ export {
560
+ href,
561
+ type ValidPaths,
562
+ type PatternToPath,
563
+ type PathResponse,
564
+ } from "./href-client.js";
609
565
 
610
566
  // Response envelope types for consuming JSON response routes
611
567
  export type { ResponseEnvelope, ResponseError } from "./urls.js";
@@ -624,8 +580,10 @@ export type { ResponseEnvelope, ResponseError } from "./urls.js";
624
580
  * ```
625
581
  */
626
582
  export function isResponseError<T>(
627
- result: import("./urls.js").ResponseEnvelope<T>
628
- ): result is import("./urls.js").ResponseEnvelope<T> & { error: import("./urls.js").ResponseError } {
583
+ result: import("./urls.js").ResponseEnvelope<T>,
584
+ ): result is import("./urls.js").ResponseEnvelope<T> & {
585
+ error: import("./urls.js").ResponseError;
586
+ } {
629
587
  return result.error !== undefined;
630
588
  }
631
589
 
@@ -33,7 +33,7 @@ const CLIENT_REFERENCE = Symbol.for("react.client.reference");
33
33
  * ```
34
34
  */
35
35
  export function isClientComponent(
36
- component: ComponentType<unknown> | unknown
36
+ component: ComponentType<unknown> | unknown,
37
37
  ): boolean {
38
38
  if (typeof component !== "function") {
39
39
  return false;
@@ -52,13 +52,13 @@ export function isClientComponent(
52
52
  */
53
53
  export function assertClientComponent(
54
54
  component: ComponentType<unknown> | unknown,
55
- name: string
55
+ name: string,
56
56
  ): asserts component is ComponentType<unknown> {
57
57
  if (typeof component !== "function") {
58
58
  throw new Error(
59
59
  `${name} must be a client component function with "use client" directive. ` +
60
60
  `Make sure to pass the component itself, not a JSX element: ` +
61
- `${name}: My${capitalize(name)} (correct) vs ${name}: <My${capitalize(name)} /> (incorrect)`
61
+ `${name}: My${capitalize(name)} (correct) vs ${name}: <My${capitalize(name)} /> (incorrect)`,
62
62
  );
63
63
  }
64
64
 
@@ -66,7 +66,7 @@ export function assertClientComponent(
66
66
  throw new Error(
67
67
  `${name} must be a client component with "use client" directive at the top of the file. ` +
68
68
  `Server components cannot be used as the ${name} because their function reference ` +
69
- `cannot be serialized in the RSC payload. Add "use client" to your ${name} file.`
69
+ `cannot be serialized in the RSC payload. Add "use client" to your ${name} file.`,
70
70
  );
71
71
  }
72
72
  }
@@ -11,7 +11,11 @@ import { MetaTags } from "../handles/MetaTags.js";
11
11
  * Uses suppressHydrationWarning on <html> because the theme script
12
12
  * may modify class/style attributes before React hydrates.
13
13
  */
14
- export function DefaultDocument({ children }: { children: ReactNode }): ReactElement {
14
+ export function DefaultDocument({
15
+ children,
16
+ }: {
17
+ children: ReactNode;
18
+ }): ReactElement {
15
19
  return (
16
20
  <html lang="en" suppressHydrationWarning>
17
21
  <head>