@rangojs/router 0.0.0-experimental.124 → 0.0.0-experimental.126

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 (235) hide show
  1. package/README.md +6 -4
  2. package/dist/bin/rango.js +3 -4
  3. package/dist/vite/index.js +315 -68
  4. package/package.json +19 -18
  5. package/skills/breadcrumbs/SKILL.md +60 -0
  6. package/skills/hooks/SKILL.md +2 -2
  7. package/skills/route/SKILL.md +6 -0
  8. package/skills/server-actions/SKILL.md +25 -1
  9. package/skills/testing/SKILL.md +17 -17
  10. package/skills/testing/cache-prerender.md +29 -3
  11. package/skills/testing/flight.md +13 -10
  12. package/skills/testing/render-handler.md +3 -0
  13. package/skills/testing/server-tree.md +1 -1
  14. package/skills/testing/setup.md +1 -1
  15. package/src/__internal.ts +0 -65
  16. package/src/browser/action-coordinator.ts +1 -1
  17. package/src/browser/action-fence.ts +10 -0
  18. package/src/browser/event-controller.ts +1 -83
  19. package/src/browser/navigation-store-handle.ts +3 -4
  20. package/src/browser/navigation-store.ts +0 -39
  21. package/src/browser/navigation-transaction.ts +0 -32
  22. package/src/browser/partial-update.ts +23 -84
  23. package/src/browser/prefetch/cache.ts +6 -45
  24. package/src/browser/prefetch/queue.ts +6 -3
  25. package/src/browser/rango-state.ts +2 -23
  26. package/src/browser/react/Link.tsx +0 -2
  27. package/src/browser/react/NavigationProvider.tsx +2 -1
  28. package/src/browser/react/ScrollRestoration.tsx +10 -6
  29. package/src/browser/react/filter-segment-order.ts +0 -2
  30. package/src/browser/react/index.ts +0 -45
  31. package/src/browser/react/location-state-shared.ts +0 -13
  32. package/src/browser/react/location-state.ts +0 -1
  33. package/src/browser/react/use-action.ts +6 -15
  34. package/src/browser/react/use-handle.ts +0 -5
  35. package/src/browser/react/use-link-status.ts +0 -4
  36. package/src/browser/react/use-navigation.ts +0 -3
  37. package/src/browser/react/use-params.ts +0 -2
  38. package/src/browser/react/use-router.ts +2 -1
  39. package/src/browser/react/use-search-params.ts +0 -5
  40. package/src/browser/react/use-segments.ts +0 -13
  41. package/src/browser/rsc-router.tsx +10 -3
  42. package/src/browser/server-action-bridge.ts +51 -3
  43. package/src/browser/types.ts +23 -5
  44. package/src/browser/validate-redirect-origin.ts +43 -16
  45. package/src/build/index.ts +8 -9
  46. package/src/build/route-trie.ts +46 -11
  47. package/src/build/route-types/param-extraction.ts +6 -3
  48. package/src/build/route-types/router-processing.ts +0 -8
  49. package/src/cache/cache-policy.ts +0 -54
  50. package/src/cache/cache-runtime.ts +48 -24
  51. package/src/cache/cache-scope.ts +0 -27
  52. package/src/cache/cache-tag.ts +0 -37
  53. package/src/cache/cf/cf-cache-store.ts +72 -45
  54. package/src/cache/cf/index.ts +0 -24
  55. package/src/cache/document-cache.ts +10 -36
  56. package/src/cache/handle-snapshot.ts +0 -40
  57. package/src/cache/index.ts +0 -27
  58. package/src/cache/memory-segment-store.ts +0 -52
  59. package/src/cache/profile-registry.ts +6 -30
  60. package/src/cache/read-through-swr.ts +41 -11
  61. package/src/cache/segment-codec.ts +0 -16
  62. package/src/cache/types.ts +0 -98
  63. package/src/client.rsc.tsx +4 -22
  64. package/src/client.tsx +19 -32
  65. package/src/context-var.ts +12 -0
  66. package/src/defer.ts +196 -0
  67. package/src/deps/ssr.ts +0 -1
  68. package/src/handle.ts +2 -12
  69. package/src/handles/MetaTags.tsx +0 -14
  70. package/src/handles/breadcrumbs.ts +16 -5
  71. package/src/handles/meta.ts +0 -39
  72. package/src/host/cookie-handler.ts +0 -36
  73. package/src/host/errors.ts +0 -24
  74. package/src/host/index.ts +6 -0
  75. package/src/host/pattern-matcher.ts +7 -50
  76. package/src/host/router.ts +1 -65
  77. package/src/host/testing.ts +0 -16
  78. package/src/host/types.ts +6 -2
  79. package/src/href-client.ts +0 -4
  80. package/src/index.rsc.ts +27 -2
  81. package/src/index.ts +7 -0
  82. package/src/internal-debug.ts +2 -4
  83. package/src/loader.rsc.ts +4 -15
  84. package/src/loader.ts +3 -9
  85. package/src/network-error-thrower.tsx +1 -6
  86. package/src/outlet-provider.tsx +1 -5
  87. package/src/prerender/param-hash.ts +10 -11
  88. package/src/prerender/store.ts +23 -30
  89. package/src/prerender.ts +34 -0
  90. package/src/redirect-origin.ts +100 -0
  91. package/src/root-error-boundary.tsx +1 -19
  92. package/src/route-content-wrapper.tsx +1 -44
  93. package/src/route-definition/dsl-helpers.ts +7 -19
  94. package/src/route-definition/helpers-types.ts +3 -3
  95. package/src/route-definition/redirect.ts +43 -9
  96. package/src/route-definition/resolve-handler-use.ts +6 -0
  97. package/src/route-map-builder.ts +0 -16
  98. package/src/router/content-negotiation.ts +0 -13
  99. package/src/router/error-handling.ts +12 -16
  100. package/src/router/find-match.ts +4 -31
  101. package/src/router/intercept-resolution.ts +10 -1
  102. package/src/router/lazy-includes.ts +1 -57
  103. package/src/router/loader-resolution.ts +25 -23
  104. package/src/router/logging.ts +0 -6
  105. package/src/router/manifest.ts +1 -25
  106. package/src/router/match-api.ts +0 -20
  107. package/src/router/match-context.ts +0 -22
  108. package/src/router/match-handlers.ts +0 -43
  109. package/src/router/match-middleware/background-revalidation.ts +0 -7
  110. package/src/router/match-middleware/cache-lookup.ts +96 -179
  111. package/src/router/match-middleware/cache-store.ts +0 -31
  112. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  113. package/src/router/match-middleware/segment-resolution.ts +0 -22
  114. package/src/router/match-pipelines.ts +1 -42
  115. package/src/router/match-result.ts +1 -52
  116. package/src/router/metrics.ts +0 -34
  117. package/src/router/middleware-types.ts +0 -116
  118. package/src/router/middleware.ts +77 -60
  119. package/src/router/navigation-snapshot.ts +0 -51
  120. package/src/router/params-util.ts +23 -0
  121. package/src/router/pattern-matching.ts +5 -56
  122. package/src/router/prerender-match.ts +56 -51
  123. package/src/router/request-classification.ts +1 -38
  124. package/src/router/revalidation.ts +14 -62
  125. package/src/router/route-snapshot.ts +0 -1
  126. package/src/router/router-context.ts +0 -27
  127. package/src/router/router-interfaces.ts +10 -0
  128. package/src/router/segment-resolution/fresh.ts +25 -57
  129. package/src/router/segment-resolution/helpers.ts +34 -0
  130. package/src/router/segment-resolution/loader-cache.ts +35 -23
  131. package/src/router/segment-resolution/revalidation.ts +188 -283
  132. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  133. package/src/router/segment-resolution.ts +4 -1
  134. package/src/router/segment-wrappers.ts +0 -3
  135. package/src/router/telemetry-otel.ts +0 -20
  136. package/src/router/telemetry.ts +0 -22
  137. package/src/router/timeout.ts +0 -20
  138. package/src/router/trie-matching.ts +66 -45
  139. package/src/router/types.ts +1 -63
  140. package/src/router/url-params.ts +0 -5
  141. package/src/router.ts +8 -11
  142. package/src/rsc/handler-context.ts +1 -0
  143. package/src/rsc/handler.ts +20 -4
  144. package/src/rsc/helpers.ts +71 -3
  145. package/src/rsc/json-route-result.ts +38 -0
  146. package/src/rsc/origin-guard.ts +9 -15
  147. package/src/rsc/progressive-enhancement.ts +10 -1
  148. package/src/rsc/redirect-guard.ts +99 -0
  149. package/src/rsc/response-route-handler.ts +23 -18
  150. package/src/rsc/rsc-rendering.ts +2 -7
  151. package/src/rsc/runtime-warnings.ts +14 -0
  152. package/src/rsc/server-action.ts +34 -29
  153. package/src/rsc/types.ts +6 -3
  154. package/src/search-params.ts +0 -16
  155. package/src/segment-loader-promise.ts +14 -2
  156. package/src/segment-system.tsx +79 -88
  157. package/src/server/handle-store.ts +7 -24
  158. package/src/server/loader-registry.ts +5 -24
  159. package/src/server/request-context.ts +29 -92
  160. package/src/ssr/index.tsx +14 -14
  161. package/src/static-handler.ts +2 -27
  162. package/src/testing/cache-status.ts +44 -48
  163. package/src/testing/collect-handle.ts +1 -24
  164. package/src/testing/dispatch.ts +43 -6
  165. package/src/testing/e2e/index.ts +1 -22
  166. package/src/testing/e2e/matchers.ts +0 -16
  167. package/src/testing/flight-matchers.ts +0 -13
  168. package/src/testing/flight-normalize.ts +3 -30
  169. package/src/testing/flight.ts +46 -48
  170. package/src/testing/generated-routes.ts +1 -41
  171. package/src/testing/index.ts +1 -21
  172. package/src/testing/internal/context.ts +3 -45
  173. package/src/testing/internal/seed-vars.ts +0 -26
  174. package/src/testing/render-handler.ts +31 -61
  175. package/src/testing/render-route.tsx +75 -103
  176. package/src/testing/run-loader.ts +0 -96
  177. package/src/testing/run-middleware.ts +0 -26
  178. package/src/theme/ThemeProvider.tsx +0 -52
  179. package/src/theme/ThemeScript.tsx +0 -6
  180. package/src/theme/constants.ts +0 -12
  181. package/src/theme/index.ts +0 -7
  182. package/src/theme/theme-context.ts +1 -5
  183. package/src/theme/theme-script.ts +0 -14
  184. package/src/theme/use-theme.ts +0 -3
  185. package/src/types/boundaries.ts +0 -35
  186. package/src/types/error-types.ts +25 -89
  187. package/src/types/global-namespace.ts +4 -14
  188. package/src/types/handler-context.ts +28 -9
  189. package/src/types/index.ts +0 -10
  190. package/src/types/request-scope.ts +0 -19
  191. package/src/types/route-config.ts +6 -50
  192. package/src/types/route-entry.ts +0 -6
  193. package/src/types/segments.ts +0 -13
  194. package/src/urls/include-helper.ts +0 -4
  195. package/src/urls/index.ts +0 -6
  196. package/src/urls/path-helper-types.ts +2 -2
  197. package/src/urls/path-helper.ts +0 -54
  198. package/src/urls/urls-function.ts +0 -13
  199. package/src/use-loader.tsx +0 -186
  200. package/src/vite/discovery/bundle-postprocess.ts +2 -1
  201. package/src/vite/discovery/discover-routers.ts +28 -18
  202. package/src/vite/discovery/prerender-collection.ts +2 -4
  203. package/src/vite/discovery/state.ts +5 -0
  204. package/src/vite/discovery/virtual-module-codegen.ts +1 -11
  205. package/src/vite/plugin-types.ts +35 -9
  206. package/src/vite/plugins/cjs-to-esm.ts +0 -11
  207. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  208. package/src/vite/plugins/client-ref-hashing.ts +0 -10
  209. package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
  210. package/src/vite/plugins/expose-action-id.ts +2 -73
  211. package/src/vite/plugins/expose-id-utils.ts +0 -55
  212. package/src/vite/plugins/expose-ids/export-analysis.ts +0 -38
  213. package/src/vite/plugins/expose-ids/handler-transform.ts +0 -15
  214. package/src/vite/plugins/expose-ids/loader-transform.ts +0 -15
  215. package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
  216. package/src/vite/plugins/expose-internal-ids.ts +10 -0
  217. package/src/vite/plugins/performance-tracks.ts +0 -3
  218. package/src/vite/plugins/refresh-cmd.ts +1 -1
  219. package/src/vite/plugins/use-cache-transform.ts +21 -46
  220. package/src/vite/plugins/version-injector.ts +0 -20
  221. package/src/vite/plugins/version-plugin.ts +1 -49
  222. package/src/vite/plugins/virtual-entries.ts +0 -15
  223. package/src/vite/rango.ts +2 -108
  224. package/src/vite/router-discovery.ts +9 -1
  225. package/src/vite/utils/ast-handler-extract.ts +0 -16
  226. package/src/vite/utils/bundle-analysis.ts +6 -13
  227. package/src/vite/utils/client-chunks.ts +0 -6
  228. package/src/vite/utils/forward-user-plugins.ts +0 -22
  229. package/src/vite/utils/manifest-utils.ts +0 -4
  230. package/src/vite/utils/package-resolution.ts +1 -73
  231. package/src/vite/utils/prerender-utils.ts +0 -35
  232. package/src/vite/utils/shared-utils.ts +3 -35
  233. package/src/browser/shallow.ts +0 -40
  234. package/src/handles/index.ts +0 -7
  235. package/src/router/middleware-cookies.ts +0 -55
@@ -13,10 +13,15 @@
13
13
 
14
14
  import type { CacheItemResult, CacheItemOptions } from "./types.js";
15
15
  import { runBackground } from "./background-task.js";
16
+ import { reportCacheError } from "./cache-error.js";
17
+ import type { CacheErrorReporter } from "./cache-error.js";
16
18
 
17
- interface WaitUntilHost {
19
+ // The host carries both the optional waitUntil (for background scheduling) and
20
+ // the CacheErrorReporter seam (for routing degradation errors through onError).
21
+ // loader-cache.ts passes the request context, which provides both.
22
+ type WaitUntilHost = {
18
23
  waitUntil?: (fn: () => Promise<void>) => void;
19
- }
24
+ } & CacheErrorReporter;
20
25
 
21
26
  export interface ReadThroughItemConfig<T> {
22
27
  /** Retrieve a cached item by key */
@@ -74,11 +79,20 @@ export async function readThroughItem<T>(
74
79
  host,
75
80
  } = config;
76
81
 
77
- // Cache lookup
82
+ // Cache lookup. An infra read failure (getItem) is reported by the store
83
+ // itself, so here we just degrade to a miss. A deserialize failure is a
84
+ // corrupt/truncated stored entry, which this layer owns: report it LOUD as
85
+ // cache-corrupt, then fall through to a fresh execution (the miss-path write
86
+ // self-heals the bad entry).
87
+ let cached: CacheItemResult | null = null;
78
88
  try {
79
- const cached = await getItem(key);
89
+ cached = await getItem(key);
90
+ } catch {
91
+ cached = null;
92
+ }
80
93
 
81
- if (cached) {
94
+ if (cached) {
95
+ try {
82
96
  const data = await deserialize(cached.value);
83
97
 
84
98
  if (!cached.shouldRevalidate) {
@@ -97,16 +111,27 @@ export async function readThroughItem<T>(
97
111
  if (serialized !== null) {
98
112
  await setItem(key, serialized, storeOptions);
99
113
  }
100
- } catch {
101
- // Background revalidation failed silently
114
+ } catch (error) {
115
+ reportCacheError(
116
+ error,
117
+ "stale-revalidation",
118
+ "[read-through] background revalidation",
119
+ host ?? undefined,
120
+ );
102
121
  }
103
122
  },
104
123
  true,
105
124
  );
106
125
  return data;
126
+ } catch (error) {
127
+ reportCacheError(
128
+ error,
129
+ "cache-corrupt",
130
+ "[read-through] deserialize stored entry",
131
+ host ?? undefined,
132
+ );
133
+ // fall through to fresh execution
107
134
  }
108
- } catch {
109
- // Cache lookup failed, fall through to fresh execution
110
135
  }
111
136
 
112
137
  // Cache miss
@@ -123,8 +148,13 @@ export async function readThroughItem<T>(
123
148
  await setItem(key, serialized, storeOptions);
124
149
  onCached?.();
125
150
  }
126
- } catch {
127
- // Cache write failed silently
151
+ } catch (error) {
152
+ reportCacheError(
153
+ error,
154
+ "cache-write",
155
+ "[read-through] cache write",
156
+ host ?? undefined,
157
+ );
128
158
  }
129
159
  },
130
160
  true,
@@ -16,10 +16,6 @@ import {
16
16
  } from "@vitejs/plugin-rsc/rsc";
17
17
  import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc";
18
18
 
19
- // ============================================================================
20
- // Stream Utilities (internal)
21
- // ============================================================================
22
-
23
19
  /**
24
20
  * Convert a ReadableStream to a string.
25
21
  */
@@ -55,10 +51,6 @@ export function stringToStream(str: string): ReadableStream<Uint8Array> {
55
51
  });
56
52
  }
57
53
 
58
- // ============================================================================
59
- // RSC Serialization Primitives (internal)
60
- // ============================================================================
61
-
62
54
  /**
63
55
  * RSC-serialize a value using React Server Components stream.
64
56
  * Used for serializing loaderData, layout, loading components etc.
@@ -90,10 +82,6 @@ export async function rscDeserialize<T>(
90
82
  return createFromReadableStream<T>(stream, { temporaryReferences });
91
83
  }
92
84
 
93
- // ============================================================================
94
- // Null-Preserving RSC Serialization (for caching)
95
- // ============================================================================
96
-
97
85
  /**
98
86
  * RSC-serialize any value including null.
99
87
  * Unlike rscSerialize(), this does NOT skip null — it serializes it through
@@ -122,10 +110,6 @@ export async function deserializeResult<T>(encoded: string): Promise<T> {
122
110
  return createFromReadableStream<T>(stream, { temporaryReferences });
123
111
  }
124
112
 
125
- // ============================================================================
126
- // Public API
127
- // ============================================================================
128
-
129
113
  /**
130
114
  * RSC-deserialize a single encoded component string back to a React element.
131
115
  * Used by the static handler runtime to revive pre-rendered components.
@@ -12,10 +12,6 @@
12
12
  import type { ResolvedSegment } from "../types.js";
13
13
  import type { RequestContext } from "../server/request-context.js";
14
14
 
15
- // ============================================================================
16
- // Segment Cache Store (low-level storage interface)
17
- // ============================================================================
18
-
19
15
  /**
20
16
  * Result from cache get() including data and revalidation status
21
17
  */
@@ -116,12 +112,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
116
112
  */
117
113
  clear?(): Promise<void>;
118
114
 
119
- // ============================================================================
120
- // Document Cache Methods (optional)
121
- // ============================================================================
122
- // These methods are for caching full HTTP responses (document-level caching).
123
- // Stores that support response caching should implement these methods.
124
-
125
115
  /**
126
116
  * Get a cached Response by key.
127
117
  * Returns the response and whether it should be revalidated (SWR).
@@ -146,12 +136,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
146
136
  tags?: string[],
147
137
  ): Promise<void>;
148
138
 
149
- // ============================================================================
150
- // Function Cache Methods (optional, for "use cache" directive)
151
- // ============================================================================
152
- // These methods cache individual function/component return values.
153
- // Stores that support "use cache" should implement these methods.
154
-
155
139
  /**
156
140
  * Get a cached function result by key.
157
141
  * Returns the serialized value, optional handle data, and staleness flag.
@@ -170,10 +154,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
170
154
  options?: CacheItemOptions,
171
155
  ): Promise<void>;
172
156
 
173
- // ============================================================================
174
- // Tag-based Invalidation (optional)
175
- // ============================================================================
176
-
177
157
  /**
178
158
  * Invalidate every cache entry (segment, response, item) tagged with any of
179
159
  * `tags`. Store-level primitive that the public updateTag()/revalidateTag()
@@ -264,10 +244,6 @@ export interface CachedEntryData {
264
244
  taggedAt?: number;
265
245
  }
266
246
 
267
- // ============================================================================
268
- // Cache Configuration
269
- // ============================================================================
270
-
271
247
  /**
272
248
  * Default cache options applied to all cache() boundaries.
273
249
  * Individual cache() calls can override any of these values.
@@ -292,82 +268,8 @@ export interface CacheDefaults {
292
268
  swr?: number;
293
269
  }
294
270
 
295
- /**
296
- * Cache configuration for RSC handler
297
- */
298
- export interface CacheConfig {
299
- /** Cache store implementation (includes defaults) */
300
- store: SegmentCacheStore;
301
- /** Enable/disable caching (default: true) */
302
- enabled?: boolean;
303
- }
304
-
305
- /**
306
- * Cache configuration - can be static or a function receiving env
307
- */
308
- export type CacheConfigOrFactory<TEnv> =
309
- | CacheConfig
310
- | ((env: TEnv) => CacheConfig);
311
-
312
- // ============================================================================
313
- // Segment Cache Provider (request-level interface)
314
- // ============================================================================
315
-
316
271
  /**
317
272
  * Handle data for a single segment
318
273
  * Structure: { handleName: [values...] }
319
274
  */
320
275
  export type SegmentHandleData = Record<string, unknown[]>;
321
-
322
- /**
323
- * Result from cache get() including segments and their handle data
324
- * Each entry can produce multiple segments (main + parallels)
325
- */
326
- export interface CachedEntryResult {
327
- /** All segments for this entry (main segment + parallels) */
328
- segments: ResolvedSegment[];
329
- /** Handle data keyed by segment ID */
330
- handles: Record<string, SegmentHandleData>;
331
- }
332
-
333
- /**
334
- * Segment cache provider interface
335
- *
336
- * Used by router to check/store segment cache during matching.
337
- * Accessed via request context - if not present, caching is disabled.
338
- *
339
- * @internal Not currently implemented - CacheScope is used directly.
340
- * Reserved for future extensibility.
341
- */
342
- export interface SegmentCacheProvider {
343
- /** Whether caching is enabled for this request */
344
- readonly enabled: boolean;
345
-
346
- /**
347
- * Get cached segments and restore handles/loaders.
348
- *
349
- * Combines cache get with handle replay and loader data restoration.
350
- * Returns tuple of [segments, segmentIds] if cache hit, null if miss or disabled.
351
- *
352
- * @param cacheKey - Cache key to look up
353
- * @param params - Route params for cache key generation
354
- * @param loaderPromises - Map to restore loader data into
355
- * @returns Tuple of [segments, segmentIds] or null if miss
356
- */
357
- restore(
358
- cacheKey: string,
359
- params: Record<string, string>,
360
- loaderPromises: Map<string, Promise<any>>,
361
- ): Promise<[ResolvedSegment[], string[]] | null>;
362
-
363
- /**
364
- * Cache entry with automatic handle collection (non-blocking).
365
- *
366
- * Schedules caching via waitUntil - handles are collected after they settle.
367
- * Validates segments have actual components before caching.
368
- *
369
- * @param cacheKey - The cache key to store under
370
- * @param segments - All resolved segments for this entry
371
- */
372
- cacheEntry(cacheKey: string, segments: ResolvedSegment[]): void;
373
- }
@@ -14,60 +14,43 @@
14
14
  export {
15
15
  Outlet,
16
16
  ParallelOutlet,
17
- OutletProvider,
18
17
  useOutlet,
19
18
  useLoader,
20
19
  ErrorBoundary,
21
20
  type ErrorBoundaryProps,
22
21
  } from "./client.js";
23
22
 
24
- // Re-export the server's createLoader for RSC context
25
- // This version includes the actual loader function
26
23
  export { createLoader } from "./route-definition.js";
27
24
 
28
- // Re-export Link component (can be used in server components)
29
25
  export {
30
26
  Link,
31
27
  type LinkProps,
32
28
  type PrefetchStrategy,
33
29
  } from "./browser/react/Link.js";
34
30
 
35
- // Re-export ScrollRestoration (can be used in server components)
36
31
  export {
37
32
  ScrollRestoration,
38
33
  type ScrollRestorationProps,
39
34
  } from "./browser/react/ScrollRestoration.js";
40
35
 
41
- // Re-export NavigationProvider (needed for setup)
42
36
  export {
43
37
  NavigationProvider,
44
38
  type NavigationProviderProps,
45
39
  } from "./browser/react/NavigationProvider.js";
46
40
 
47
- // Re-export href function (can be used in server components)
48
41
  export { href } from "./href-client.js";
49
42
 
50
- // Mount context re-exports (useMount is client-only, but MountContext can be referenced)
51
43
  export { MountContext } from "./browser/react/mount-context.js";
52
44
 
53
- // Note: useNavigation and useAction are NOT re-exported here
54
- // because they use client-side state and should only be used in client components
45
+ // useNavigation and useAction are NOT re-exported here because they use client-side state
55
46
 
56
- // Handle API - for accumulating data across route segments
57
- // Works in both RSC and client contexts
58
47
  export { createHandle, isHandle, type Handle } from "./handle.js";
59
48
 
60
- // Built-in handles
61
- // Meta handle works in RSC context
62
49
  export { Meta } from "./handles/meta.js";
63
- // MetaTags is a "use client" component that can be imported from RSC
64
50
  export { MetaTags } from "./handles/MetaTags.js";
65
51
  export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
66
- // Breadcrumbs handle works in RSC context
67
52
  export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
68
53
 
69
- // Location state - createLocationState works in RSC (just creates definition)
70
- // useLocationState is NOT exported here as it uses client hooks
71
54
  export {
72
55
  createLocationState,
73
56
  type LocationStateDefinition,
@@ -75,14 +58,13 @@ export {
75
58
  type LocationStateOptions,
76
59
  } from "./browser/react/location-state-shared.js";
77
60
 
78
- // Re-export useHref - it's a "use client" hook
79
61
  export { useHref } from "./browser/react/use-href.js";
80
62
 
81
- // Re-export useReverse - it's a "use client" hook
82
63
  export { useReverse } from "./browser/react/use-reverse.js";
83
64
 
84
- // Re-export useHandle - it's a "use client" hook
85
65
  export { useHandle } from "./browser/react/use-handle.js";
66
+ // Type a deferred-aware consumer narrows: an accumulated entry may be a Promise
67
+ // (a `ctx.use(Handle).defer()` slot) until it resolves.
68
+ export type { DeferredHandleEntry } from "./defer.js";
86
69
 
87
- // Re-export useLocationState - it's a "use client" hook
88
70
  export { useLocationState } from "./browser/react/location-state.js";
package/src/client.tsx CHANGED
@@ -111,6 +111,11 @@ function useSlotSegment(
111
111
  * the parallel segment with that slot name instead of the default content.
112
112
  * This is used for parallel routes and intercepting routes.
113
113
  *
114
+ * For a named slot, `<Outlet name="@x" />` is equivalent to
115
+ * `<ParallelOutlet name="@x" />` — both run the same resolution + wrapping
116
+ * pipeline. Convention: use bare `<Outlet />` for default content and
117
+ * `<ParallelOutlet name="@x" />` for named slots.
118
+ *
114
119
  * @param name - Optional slot name for parallel/intercept content (must start with @)
115
120
  *
116
121
  * @example
@@ -163,6 +168,9 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
163
168
  * is wrapped in Suspense with the loading component as fallback.
164
169
  * This enables streaming and navigation loading states for parallels.
165
170
  *
171
+ * Equivalent to `<Outlet name="@x" />` for a named slot; ParallelOutlet
172
+ * requires `name` and is named-slot-only, which reads clearer at the call site.
173
+ *
166
174
  * @param name - The slot name (must start with @, e.g., "@modal", "@sidebar")
167
175
  *
168
176
  * @example
@@ -186,10 +194,9 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
186
194
  }
187
195
 
188
196
  // OutletProvider is defined in outlet-provider.tsx to break a circular
189
- // dependency between client.tsx and route-content-wrapper.tsx.
190
- // Imported at the top of this file for local use in Outlet/ParallelOutlet,
191
- // and re-exported here for backwards compatibility.
192
- export { OutletProvider };
197
+ // dependency between client.tsx and route-content-wrapper.tsx. It is imported
198
+ // at the top of this file for local use in Outlet/ParallelOutlet only; it is an
199
+ // internal component and is intentionally not part of the public ./client API.
193
200
 
194
201
  /**
195
202
  * Hook to access outlet content programmatically
@@ -210,7 +217,6 @@ export function useOutlet(): ReactNode {
210
217
  return context?.content ?? null;
211
218
  }
212
219
 
213
- // Loader hooks - re-exported from dedicated file
214
220
  export {
215
221
  useLoader,
216
222
  useFetchLoader,
@@ -329,12 +335,6 @@ export class ErrorBoundary extends Component<
329
335
  }
330
336
  }
331
337
 
332
- // ============================================================================
333
- // Re-exports from browser/react for convenience
334
- // These are the most commonly used client-side navigation utilities
335
- // ============================================================================
336
-
337
- // Navigation hooks
338
338
  export { useNavigation } from "./browser/react/use-navigation.js";
339
339
  export { useRouter } from "./browser/react/use-router.js";
340
340
  export { usePathname } from "./browser/react/use-pathname.js";
@@ -344,58 +344,55 @@ export type {
344
344
  RouterInstance,
345
345
  RouterNavigateOptions,
346
346
  ReadonlyURLSearchParams,
347
+ ActionState,
348
+ ActionLifecycleState,
347
349
  } from "./browser/types.js";
348
350
 
349
- // Action state tracking hook
350
351
  export {
351
352
  useAction,
352
353
  type ServerActionFunction,
353
354
  } from "./browser/react/use-action.js";
354
355
 
355
- // Segments state hook
356
356
  export {
357
357
  useSegments,
358
358
  type SegmentsState,
359
359
  } from "./browser/react/use-segments.js";
360
360
 
361
- // Provider
362
361
  export {
363
362
  NavigationProvider,
364
363
  type NavigationProviderProps,
365
364
  } from "./browser/react/NavigationProvider.js";
366
365
 
367
- // Link component
368
366
  export {
369
367
  Link,
370
368
  type LinkProps,
371
369
  type PrefetchStrategy,
372
370
  type StateOrGetter,
371
+ type LinkState,
373
372
  } from "./browser/react/Link.js";
374
373
 
375
- // Link status hook
376
374
  export {
377
375
  useLinkStatus,
378
376
  type LinkStatus,
379
377
  } from "./browser/react/use-link-status.js";
380
378
 
381
- // Scroll restoration
382
379
  export {
383
380
  ScrollRestoration,
384
381
  useScrollRestoration,
385
382
  type ScrollRestorationProps,
386
383
  } from "./browser/react/ScrollRestoration.js";
387
384
 
388
- // Handle data hook (client-side only — createHandle/isHandle are server APIs from the root export)
389
385
  export { type Handle } from "./handle.js";
390
386
  export { useHandle } from "./browser/react/use-handle.js";
387
+ // Type a deferred-aware consumer narrows: an accumulated entry may be a Promise
388
+ // (a `ctx.use(Handle).defer()` slot) until it resolves.
389
+ export type { DeferredHandleEntry } from "./defer.js";
391
390
 
392
- // Built-in handles
393
391
  export { Meta } from "./handles/meta.js";
394
392
  export { MetaTags } from "./handles/MetaTags.js";
395
393
  export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
396
394
  export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
397
395
 
398
- // Location state - type-safe navigation state
399
396
  export {
400
397
  createLocationState,
401
398
  useLocationState,
@@ -404,29 +401,19 @@ export {
404
401
  type LocationStateOptions,
405
402
  } from "./browser/react/location-state.js";
406
403
 
407
- // Type-safe href for client-side path validation. The path and response types
408
- // are ambient as `Rango.Path` / `Rango.PathResponse` (declared in
409
- // href-client.ts) — no import needed.
404
+ // Ambient Rango.Path / Rango.PathResponse types (declared in href-client.ts)
410
405
  export { href, type PatternToPath } from "./href-client.js";
411
406
 
412
- // Problem Details (RFC 9457) error body type for consuming JSON response routes.
413
- // On a non-2xx response, `await res.json()` yields this shape; on success the
414
- // body is the bare value (no envelope). Discriminate on `res.ok` / status.
407
+ // RFC 9457 error type for JSON response routes
415
408
  export type { ProblemDetails } from "./urls.js";
416
409
 
417
- // Mount context for include() scoped components
418
410
  export { useMount } from "./browser/react/use-mount.js";
419
411
  export { MountContext } from "./browser/react/mount-context.js";
420
412
 
421
- // Mount-aware href hook - auto-prefixes paths with include() mount
422
413
  export { useHref } from "./browser/react/use-href.js";
423
414
 
424
- // Mount-aware reverse hook - resolves dot-prefixed names against an imported
425
- // generated routes map (from a urls() module's .gen.ts).
426
415
  export { useReverse } from "./browser/react/use-reverse.js";
427
416
 
428
- // Type-safe scoped reverse function for scopedReverse<typeof patterns>()
429
417
  export type { ScopedReverseFunction, LocalReverseFunction } from "./reverse.js";
430
418
 
431
- // Loader definition type - for typing loader props in client components
432
419
  export type { LoaderDefinition } from "./types.js";
@@ -70,6 +70,18 @@ export function isContextVar(value: unknown): value is ContextVar<unknown> {
70
70
  );
71
71
  }
72
72
 
73
+ /**
74
+ * Does a variables object hold any entries? Counts both string keys and the
75
+ * symbol-keyed entries (context vars are stored under symbols), so an object
76
+ * carrying only symbol-keyed vars is still reported as non-empty.
77
+ */
78
+ export function hasContextVars(variables: object): boolean {
79
+ return (
80
+ Object.keys(variables).length > 0 ||
81
+ Object.getOwnPropertySymbols(variables).length > 0
82
+ );
83
+ }
84
+
73
85
  /**
74
86
  * Symbol used as a Set stored on the variables object to track
75
87
  * which keys hold non-cacheable values (from write-level { cache: false }).