@rangojs/router 0.0.0-experimental.32 → 0.0.0-experimental.33

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.
package/README.md CHANGED
@@ -275,7 +275,8 @@ All handler typing styles are supported, but they solve different problems:
275
275
  Example of a scoped local name inside a mounted module:
276
276
 
277
277
  ```tsx
278
- import type { Handler, ScopedRouteMap } from "@rangojs/router";
278
+ import type { Handler } from "@rangojs/router";
279
+ import type { ScopedRouteMap } from "@rangojs/router/__internal";
279
280
 
280
281
  type BlogRoutes = ScopedRouteMap<"blog">;
281
282
 
@@ -1745,7 +1745,7 @@ import { resolve } from "node:path";
1745
1745
  // package.json
1746
1746
  var package_default = {
1747
1747
  name: "@rangojs/router",
1748
- version: "0.0.0-experimental.32",
1748
+ version: "0.0.0-experimental.33",
1749
1749
  description: "Django-inspired RSC router with composable URL patterns",
1750
1750
  keywords: [
1751
1751
  "react",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rangojs/router",
3
- "version": "0.0.0-experimental.32",
3
+ "version": "0.0.0-experimental.33",
4
4
  "description": "Django-inspired RSC router with composable URL patterns",
5
5
  "keywords": [
6
6
  "react",
@@ -204,3 +204,47 @@ export const urlpatterns = urls(({ path, layout }) => [
204
204
  ```
205
205
 
206
206
  Navigating to `/shop/widget` produces: `Home / Shop / widget`
207
+
208
+ ## Custom Handles
209
+
210
+ Create your own handle with `createHandle()`:
211
+
212
+ ```typescript
213
+ import { createHandle } from "@rangojs/router";
214
+
215
+ // Default: flatten into array
216
+ export const PageTitle = createHandle<string, string>(
217
+ (segments) => segments.flat().at(-1) ?? "Default Title",
218
+ );
219
+
220
+ // No collect function: default flattens into T[]
221
+ export const Warnings = createHandle<string>();
222
+ ```
223
+
224
+ The Vite `exposeInternalIds` plugin auto-injects a stable `$$id` based on
225
+ file path and export name. No manual naming required for project-local code.
226
+
227
+ ### Handles in 3rd-party packages
228
+
229
+ The `exposeInternalIds` plugin skips `node_modules/`, so handles defined in
230
+ published packages won't get auto-injected IDs. Pass a manual tag as the
231
+ second argument to `createHandle()`:
232
+
233
+ ```typescript
234
+ import { createHandle } from "@rangojs/router";
235
+
236
+ // With a collect function (reducer): collect is first arg, tag is second
237
+ export const Breadcrumbs = createHandle<BreadcrumbItem, BreadcrumbItem[]>(
238
+ collectBreadcrumbs,
239
+ "__my_package_breadcrumbs__",
240
+ );
241
+
242
+ // Without a collect function: pass undefined, then the tag
243
+ export const Warnings = createHandle<string>(
244
+ undefined,
245
+ "__my_package_warnings__",
246
+ );
247
+ ```
248
+
249
+ The tag must be globally unique and stable across builds. Without it,
250
+ `createHandle` throws in development mode.
@@ -58,6 +58,26 @@ function NavigationControls() {
58
58
  }
59
59
  ```
60
60
 
61
+ #### Skipping revalidation
62
+
63
+ Pass `revalidate: false` to skip the RSC server fetch for same-pathname navigations (search param or hash changes). The URL updates and all hooks re-render, but server components stay as-is.
64
+
65
+ ```tsx
66
+ // Update search params without server round-trip
67
+ router.push("/products?color=blue", { revalidate: false });
68
+ router.replace("/products?page=3", { revalidate: false });
69
+ ```
70
+
71
+ If the pathname changes, `revalidate: false` is silently ignored and a full navigation occurs. This also works on `<Link>`:
72
+
73
+ ```tsx
74
+ <Link to="/products?color=blue" revalidate={false}>
75
+ Blue
76
+ </Link>
77
+ ```
78
+
79
+ Plain `<a>` tags can opt in via `data-revalidate="false"`.
80
+
61
81
  ### useSegments()
62
82
 
63
83
  Access current URL path and matched route segments:
@@ -65,19 +65,24 @@ export const urlpatterns = urls(({ path, loader }) => [
65
65
 
66
66
  ## Consuming Loader Data
67
67
 
68
- ### In Server Components
68
+ Loaders are the **live data layer** — they resolve fresh on every request.
69
+ The way you consume them depends on whether you're in a server component
70
+ (route handler) or a client component.
69
71
 
70
- ```typescript
71
- import { useLoader } from "@rangojs/router/client";
72
- import { ProductLoader } from "./loaders/product";
72
+ > **IMPORTANT: Prefer consuming loaders in client components.** Keeping data
73
+ > fetching in loaders and consumption in client components creates a clean
74
+ > separation: the server-side handler renders static markup that can be
75
+ > freely cached with `cache()`, while loader data stays fresh on every
76
+ > request. When you consume loaders in server handlers via `ctx.use()`, the
77
+ > handler output depends on the loader data, which means caching the handler
78
+ > also caches the data — defeating the purpose of the live data layer.
73
79
 
74
- async function ProductPage() {
75
- const { product } = await useLoader(ProductLoader);
76
- return <h1>{product.name}</h1>;
77
- }
78
- ```
80
+ ### In Client Components (Preferred)
79
81
 
80
- ### In Client Components
82
+ Client components use `useLoader()` from `@rangojs/router/client`.
83
+ The loader **must** be registered with `loader()` in the route's DSL
84
+ segments so the framework knows to resolve it during SSR and stream
85
+ the data to the client:
81
86
 
82
87
  ```typescript
83
88
  "use client";
@@ -90,6 +95,42 @@ function ProductDetails() {
90
95
  }
91
96
  ```
92
97
 
98
+ ```typescript
99
+ // Route definition — loader() registration required for client consumption
100
+ path("/product/:slug", ProductPage, { name: "product" }, () => [
101
+ loader(ProductLoader), // Required for useLoader() in client components
102
+ ]);
103
+ ```
104
+
105
+ ### In Route Handlers (Server Components)
106
+
107
+ In server components, use `ctx.use(Loader)` directly in the route handler.
108
+ This doesn't require `loader()` registration in the DSL — it works
109
+ standalone. **However**, prefer client-side consumption when possible (see
110
+ note above).
111
+
112
+ ```typescript
113
+ import { ProductLoader } from "./loaders/product";
114
+
115
+ // Route handler — server component
116
+ path("/product/:slug", async (ctx) => {
117
+ const { product } = await ctx.use(ProductLoader);
118
+ return <h1>{product.name}</h1>;
119
+ }, { name: "product" })
120
+ ```
121
+
122
+ When you do register with `loader()` in the DSL, `ctx.use()` returns the
123
+ same memoized result — loaders never run twice per request.
124
+
125
+ **Never use `useLoader()` in server components** — it is a client-only API.
126
+
127
+ ### Summary
128
+
129
+ | Context | API | `loader()` DSL required? |
130
+ | ---------------------------- | ------------------- | ------------------------ |
131
+ | Client component (preferred) | `useLoader(Loader)` | **Yes** |
132
+ | Route handler (server) | `ctx.use(Loader)` | No |
133
+
93
134
  ## Loader Context
94
135
 
95
136
  Loaders receive the same context as route handlers:
@@ -538,13 +579,12 @@ export const urlpatterns = urls(({ path, layout, loader, loading, cache, revalid
538
579
  ]),
539
580
  ]);
540
581
 
541
- // pages/product.tsx
542
- import { useLoader } from "@rangojs/router/client";
582
+ // pages/product.tsx — server component (route handler)
543
583
  import { ProductLoader, CartLoader } from "./loaders/shop";
544
584
 
545
- async function ProductPage() {
546
- const { product } = await useLoader(ProductLoader);
547
- const { cart } = await useLoader(CartLoader);
585
+ async function ProductPage(ctx) {
586
+ const { product } = await ctx.use(ProductLoader);
587
+ const { cart } = await ctx.use(CartLoader);
548
588
 
549
589
  return (
550
590
  <div>
package/src/__internal.ts CHANGED
@@ -164,6 +164,98 @@ export type {
164
164
  */
165
165
  export type { InternalHandlerContext } from "./types.js";
166
166
 
167
+ // ============================================================================
168
+ // Rendering (Internal)
169
+ // ============================================================================
170
+
171
+ /**
172
+ * @internal
173
+ * Builds React element trees from route segments.
174
+ */
175
+ export { renderSegments } from "./segment-system.js";
176
+
177
+ // ============================================================================
178
+ // Error Utilities (Internal)
179
+ // ============================================================================
180
+
181
+ /**
182
+ * @internal
183
+ * Error sanitization and network error utilities.
184
+ */
185
+ export { sanitizeError, NetworkError, isNetworkError } from "./errors.js";
186
+
187
+ // ============================================================================
188
+ // Type Utilities (Internal)
189
+ // ============================================================================
190
+
191
+ /**
192
+ * @internal
193
+ * Scoped view of GeneratedRouteMap for Handler<"localName", ScopedRouteMap<"prefix">>.
194
+ */
195
+ export type { ScopedRouteMap } from "./types.js";
196
+
197
+ /**
198
+ * @internal
199
+ * Type-level utilities for reverse URL generation.
200
+ */
201
+ export type { MergeRoutes, SanitizePrefix } from "./reverse.js";
202
+
203
+ /**
204
+ * @internal
205
+ * Individual telemetry event types.
206
+ */
207
+ export type {
208
+ RequestStartEvent,
209
+ RequestEndEvent,
210
+ RequestErrorEvent,
211
+ RequestTimeoutEvent,
212
+ LoaderStartEvent,
213
+ LoaderEndEvent,
214
+ LoaderErrorEvent,
215
+ HandlerErrorEvent,
216
+ CacheDecisionEvent,
217
+ RevalidationDecisionEvent,
218
+ } from "./router/telemetry.js";
219
+
220
+ // ============================================================================
221
+ // Pre-render / Static Handler Guards (Internal)
222
+ // ============================================================================
223
+
224
+ /**
225
+ * @internal
226
+ * Type guard for prerender handler definitions.
227
+ */
228
+ export { isPrerenderHandler } from "./prerender.js";
229
+
230
+ /**
231
+ * @internal
232
+ * Type guard for static handler definitions.
233
+ */
234
+ export { isStaticHandler } from "./static-handler.js";
235
+
236
+ // ============================================================================
237
+ // URL Pattern Internals
238
+ // ============================================================================
239
+
240
+ /**
241
+ * @internal
242
+ * Sentinel used to tag response-type route entries.
243
+ */
244
+ export { RESPONSE_TYPE } from "./urls.js";
245
+
246
+ // ============================================================================
247
+ // Route Match Debug (Internal)
248
+ // ============================================================================
249
+
250
+ /**
251
+ * @internal
252
+ * Debug utilities for route matching performance analysis.
253
+ */
254
+ export {
255
+ enableMatchDebug,
256
+ getMatchDebugStats,
257
+ } from "./router/pattern-matching.js";
258
+
167
259
  // ============================================================================
168
260
  // Debug Utilities (Internal)
169
261
  // ============================================================================
@@ -117,6 +117,7 @@ export function setupLinkInterception(
117
117
  // Read navigation options from data attributes (set by Link component)
118
118
  const scrollAttr = link.getAttribute("data-scroll");
119
119
  const replaceAttr = link.getAttribute("data-replace");
120
+ const revalidateAttr = link.getAttribute("data-revalidate");
120
121
 
121
122
  const navigateOptions: NavigateOptions = {};
122
123
  if (scrollAttr === "false") {
@@ -125,6 +126,9 @@ export function setupLinkInterception(
125
126
  if (replaceAttr === "true") {
126
127
  navigateOptions.replace = true;
127
128
  }
129
+ if (revalidateAttr === "false") {
130
+ navigateOptions.revalidate = false;
131
+ }
128
132
 
129
133
  onNavigate(href, navigateOptions);
130
134
  };
@@ -10,6 +10,12 @@ import {
10
10
  createNavigationTransaction,
11
11
  resolveNavigationState,
12
12
  } from "./navigation-transaction.js";
13
+ import { buildHistoryState } from "./history-state.js";
14
+ import {
15
+ handleNavigationStart,
16
+ handleNavigationEnd,
17
+ ensureHistoryKey,
18
+ } from "./scroll-restoration.js";
13
19
 
14
20
  // addTransitionType is only available in React experimental
15
21
  const addTransitionType: ((type: string) => void) | undefined =
@@ -18,7 +24,6 @@ const addTransitionType: ((type: string) => void) | undefined =
18
24
  import { setupLinkInterception } from "./link-interceptor.js";
19
25
  import { createPartialUpdater } from "./partial-update.js";
20
26
  import { generateHistoryKey } from "./navigation-store.js";
21
- import { handleNavigationEnd } from "./scroll-restoration.js";
22
27
  import type { EventController } from "./event-controller.js";
23
28
  import { isInterceptOnlyCache } from "./intercept-utils.js";
24
29
  import {
@@ -114,6 +119,85 @@ export function createNavigationBridge(
114
119
  return;
115
120
  }
116
121
 
122
+ // Shallow navigation: skip RSC fetch when revalidate is false
123
+ // and the pathname hasn't changed (search param / hash only change).
124
+ if (
125
+ options?.revalidate === false &&
126
+ targetUrl.pathname === new URL(window.location.href).pathname
127
+ ) {
128
+ // Preserve intercept context from the current history entry so that
129
+ // popstate uses the correct cache key (:intercept suffix) and restores
130
+ // the right full-page vs modal semantics.
131
+ const currentHistoryState = window.history.state;
132
+ const isIntercept = currentHistoryState?.intercept === true;
133
+ const interceptSourceUrl = isIntercept
134
+ ? currentHistoryState?.sourceUrl
135
+ : undefined;
136
+
137
+ const historyKey = generateHistoryKey(url, { intercept: isIntercept });
138
+
139
+ // Copy current segments to the new history key so back/forward restores instantly
140
+ const currentKey = store.getHistoryKey();
141
+ const currentCache = store.getCachedSegments(currentKey);
142
+ if (currentCache?.segments) {
143
+ const currentHandleData = eventController.getHandleState().data;
144
+ store.cacheSegmentsForHistory(
145
+ historyKey,
146
+ currentCache.segments,
147
+ currentHandleData,
148
+ );
149
+ }
150
+
151
+ // Save current scroll position before changing URL
152
+ handleNavigationStart();
153
+
154
+ // Snapshot old state before pushState/replaceState overwrites it
155
+ const oldState = window.history.state;
156
+
157
+ // Update browser URL (carry intercept context into history state)
158
+ const historyState = buildHistoryState(
159
+ resolvedState,
160
+ {
161
+ intercept: isIntercept || undefined,
162
+ sourceUrl: interceptSourceUrl,
163
+ },
164
+ {},
165
+ );
166
+ if (options.replace) {
167
+ window.history.replaceState(historyState, "", url);
168
+ } else {
169
+ window.history.pushState(historyState, "", url);
170
+ }
171
+
172
+ // Ensure new history entry has a scroll restoration key
173
+ ensureHistoryKey();
174
+
175
+ // Notify useLocationState() hooks when state changes
176
+ const hasOldState =
177
+ oldState &&
178
+ typeof oldState === "object" &&
179
+ ("state" in oldState ||
180
+ Object.keys(oldState).some((k) => k.startsWith("__rsc_ls_")));
181
+ const hasNewState =
182
+ historyState &&
183
+ ("state" in historyState ||
184
+ Object.keys(historyState).some((k) => k.startsWith("__rsc_ls_")));
185
+ if (hasOldState || hasNewState) {
186
+ window.dispatchEvent(new Event("__rsc_locationstate"));
187
+ }
188
+
189
+ // Update store history key so future navigations reference the right cache
190
+ store.setHistoryKey(historyKey);
191
+ store.setCurrentUrl(url);
192
+
193
+ // Notify hooks — location updates, state stays idle
194
+ eventController.setLocation(targetUrl);
195
+
196
+ // Handle post-navigation scroll
197
+ handleNavigationEnd({ scroll: options.scroll });
198
+ return;
199
+ }
200
+
117
201
  // Only abort pending requests when navigating to a different route
118
202
  // Same-route navigation (e.g., /todos -> /todos) should not cancel in-flight actions
119
203
  const currentPath = new URL(window.location.href).pathname;
@@ -80,6 +80,16 @@ export interface LinkProps extends Omit<
80
80
  * Force full document navigation instead of SPA
81
81
  */
82
82
  reloadDocument?: boolean;
83
+ /**
84
+ * Whether to revalidate server data on navigation.
85
+ * Set to `false` to skip the RSC server fetch and only update the URL.
86
+ *
87
+ * Only takes effect when the pathname stays the same (search param / hash changes).
88
+ * If the pathname changes, this option is ignored and a full navigation occurs.
89
+ *
90
+ * @default true
91
+ */
92
+ revalidate?: boolean;
83
93
  /**
84
94
  * Prefetch strategy for the link destination
85
95
  * @default "none"
@@ -170,6 +180,7 @@ export const Link: ForwardRefExoticComponent<
170
180
  replace = false,
171
181
  scroll = true,
172
182
  reloadDocument = false,
183
+ revalidate,
173
184
  prefetch = "none",
174
185
  state,
175
186
  children,
@@ -262,9 +273,9 @@ export const Link: ForwardRefExoticComponent<
262
273
  resolvedState = currentState;
263
274
  }
264
275
 
265
- ctx.navigate(to, { replace, scroll, state: resolvedState });
276
+ ctx.navigate(to, { replace, scroll, state: resolvedState, revalidate });
266
277
  },
267
- [to, isExternal, reloadDocument, replace, scroll, ctx, onClick],
278
+ [to, isExternal, reloadDocument, replace, scroll, revalidate, ctx, onClick],
268
279
  );
269
280
 
270
281
  const handleMouseEnter = useCallback(() => {
@@ -340,6 +351,7 @@ export const Link: ForwardRefExoticComponent<
340
351
  data-external={isExternal ? "" : undefined}
341
352
  data-scroll={scroll === false ? "false" : undefined}
342
353
  data-replace={replace ? "true" : undefined}
354
+ data-revalidate={revalidate === false ? "false" : undefined}
343
355
  {...props}
344
356
  >
345
357
  <LinkContext.Provider value={to}>{children}</LinkContext.Provider>
@@ -232,6 +232,25 @@ export type HistoryState =
232
232
  export interface NavigateOptions {
233
233
  replace?: boolean;
234
234
  scroll?: boolean;
235
+ /**
236
+ * Whether to revalidate server data on navigation.
237
+ * Set to `false` to skip the RSC server fetch and only update the URL.
238
+ *
239
+ * Only takes effect when the pathname stays the same (search param / hash changes).
240
+ * If the pathname changes, this option is ignored and a full navigation occurs.
241
+ *
242
+ * All location-aware hooks (`useSearchParams`, `useNavigation`, etc.) still update.
243
+ * Server components do not re-render.
244
+ *
245
+ * @default true
246
+ *
247
+ * @example
248
+ * ```tsx
249
+ * router.push("/products?color=blue", { revalidate: false });
250
+ * router.replace("/products?page=3", { revalidate: false });
251
+ * ```
252
+ */
253
+ revalidate?: boolean;
235
254
  /**
236
255
  * State to pass to history.pushState/replaceState
237
256
  * Accessible via useLocationState() hook.
package/src/index.rsc.ts CHANGED
@@ -11,8 +11,6 @@
11
11
 
12
12
  // Re-export all universal exports from index.ts
13
13
  export {
14
- // Universal rendering utilities
15
- renderSegments,
16
14
  // Error classes
17
15
  RouteNotFoundError,
18
16
  DataNotFoundError,
@@ -21,9 +19,6 @@ export {
21
19
  HandlerError,
22
20
  BuildError,
23
21
  InvalidHandlerError,
24
- NetworkError,
25
- isNetworkError,
26
- sanitizeError,
27
22
  RouterError,
28
23
  Skip,
29
24
  isSkip,
@@ -40,7 +35,6 @@ export type {
40
35
  TrailingSlashMode,
41
36
  // Handler types
42
37
  Handler,
43
- ScopedRouteMap,
44
38
  HandlerContext,
45
39
  ExtractParams,
46
40
  GenericParams,
@@ -120,7 +114,6 @@ export { nonce } from "./rsc/nonce.js";
120
114
  // Pre-render handler API
121
115
  export {
122
116
  Prerender,
123
- isPrerenderHandler,
124
117
  type PrerenderHandlerDefinition,
125
118
  type PrerenderPassthroughContext,
126
119
  type PrerenderOptions,
@@ -130,16 +123,11 @@ export {
130
123
  } from "./prerender.js";
131
124
 
132
125
  // Static handler API
133
- export {
134
- Static,
135
- isStaticHandler,
136
- type StaticHandlerDefinition,
137
- } from "./static-handler.js";
126
+ export { Static, type StaticHandlerDefinition } from "./static-handler.js";
138
127
 
139
128
  // Django-style URL patterns (RSC/server context)
140
129
  export {
141
130
  urls,
142
- RESPONSE_TYPE,
143
131
  type PathHelpers,
144
132
  type PathOptions,
145
133
  type UrlPatterns,
@@ -207,8 +195,6 @@ export type {
207
195
  ReverseFunction,
208
196
  ExtractLocalRoutes,
209
197
  ParamsFor,
210
- SanitizePrefix,
211
- MergeRoutes,
212
198
  } from "./reverse.js";
213
199
  export { scopedReverse, createReverse } from "./reverse.js";
214
200
 
@@ -221,12 +207,6 @@ export type {
221
207
  RouteParams,
222
208
  } from "./search-params.js";
223
209
 
224
- // Debug utilities for route matching (development only)
225
- export {
226
- enableMatchDebug,
227
- getMatchDebugStats,
228
- } from "./router/pattern-matching.js";
229
-
230
210
  // Location state (universal)
231
211
  export {
232
212
  createLocationState,
@@ -242,20 +222,7 @@ export type { PathResponse } from "./href-client.js";
242
222
  export { createConsoleSink } from "./router/telemetry.js";
243
223
  export { createOTelSink } from "./router/telemetry-otel.js";
244
224
  export type { OTelTracer, OTelSpan } from "./router/telemetry-otel.js";
245
- export type {
246
- TelemetrySink,
247
- TelemetryEvent,
248
- RequestStartEvent,
249
- RequestEndEvent,
250
- RequestErrorEvent,
251
- RequestTimeoutEvent,
252
- LoaderStartEvent,
253
- LoaderEndEvent,
254
- LoaderErrorEvent,
255
- HandlerErrorEvent,
256
- CacheDecisionEvent,
257
- RevalidationDecisionEvent,
258
- } from "./router/telemetry.js";
225
+ export type { TelemetrySink, TelemetryEvent } from "./router/telemetry.js";
259
226
 
260
227
  // Timeout types and error class
261
228
  export { RouterTimeoutError } from "./router/timeout.js";
package/src/index.ts CHANGED
@@ -10,9 +10,6 @@
10
10
  * import from "@rangojs/router/client"
11
11
  */
12
12
 
13
- // Universal rendering utilities (work on both server and client)
14
- export { renderSegments } from "./segment-system.js";
15
-
16
13
  // Error classes (can be used on both server and client)
17
14
  export {
18
15
  RouteNotFoundError,
@@ -22,9 +19,6 @@ export {
22
19
  HandlerError,
23
20
  BuildError,
24
21
  InvalidHandlerError,
25
- NetworkError,
26
- isNetworkError,
27
- sanitizeError,
28
22
  RouterError,
29
23
  Skip,
30
24
  isSkip,
@@ -41,7 +35,6 @@ export type {
41
35
  TrailingSlashMode,
42
36
  // Handler types
43
37
  Handler, // Supports params object, path pattern, or route name
44
- ScopedRouteMap, // Scoped view of GeneratedRouteMap for Handler<"localName", ScopedRouteMap<"prefix">>
45
38
  HandlerContext,
46
39
  ExtractParams,
47
40
  GenericParams,
@@ -194,20 +187,6 @@ export function createReverse(): never {
194
187
  throw serverOnlyStubError("createReverse");
195
188
  }
196
189
 
197
- /**
198
- * Error-throwing stub for server-only `enableMatchDebug` function.
199
- */
200
- export function enableMatchDebug(): never {
201
- throw serverOnlyStubError("enableMatchDebug");
202
- }
203
-
204
- /**
205
- * Error-throwing stub for server-only `getMatchDebugStats` function.
206
- */
207
- export function getMatchDebugStats(): never {
208
- throw serverOnlyStubError("getMatchDebugStats");
209
- }
210
-
211
190
  // Error-throwing stubs for server-only route helpers
212
191
  export function layout(): never {
213
192
  throw serverOnlyStubError("layout");
@@ -268,8 +247,6 @@ export type {
268
247
  ReverseFunction,
269
248
  ExtractLocalRoutes,
270
249
  ParamsFor,
271
- SanitizePrefix,
272
- MergeRoutes,
273
250
  } from "./reverse.js";
274
251
  // scopedReverse() helper for handlers to get locally-typed reverse
275
252
  export { scopedReverse } from "./reverse.js";
@@ -289,20 +266,7 @@ export type { PathResponse } from "./href-client.js";
289
266
  export { createConsoleSink } from "./router/telemetry.js";
290
267
  export { createOTelSink } from "./router/telemetry-otel.js";
291
268
  export type { OTelTracer, OTelSpan } from "./router/telemetry-otel.js";
292
- export type {
293
- TelemetrySink,
294
- TelemetryEvent,
295
- RequestStartEvent,
296
- RequestEndEvent,
297
- RequestErrorEvent,
298
- RequestTimeoutEvent,
299
- LoaderStartEvent,
300
- LoaderEndEvent,
301
- LoaderErrorEvent,
302
- HandlerErrorEvent,
303
- CacheDecisionEvent,
304
- RevalidationDecisionEvent,
305
- } from "./router/telemetry.js";
269
+ export type { TelemetrySink, TelemetryEvent } from "./router/telemetry.js";
306
270
 
307
271
  // Timeout types and error class
308
272
  export { RouterTimeoutError } from "./router/timeout.js";