@vielzeug/codex 2.3.1 → 2.3.2

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 (44) hide show
  1. package/package.json +1 -1
  2. package/data/catalog.json +0 -1886
  3. package/data/llms-full.txt +0 -32016
  4. package/data/llms.txt +0 -45
  5. package/data/manifest.json +0 -8
  6. package/data/packages/arsenal.json +0 -210
  7. package/data/packages/assay.json +0 -39
  8. package/data/packages/clockwork.json +0 -67
  9. package/data/packages/codex.json +0 -43
  10. package/data/packages/coins.json +0 -102
  11. package/data/packages/conduit.json +0 -60
  12. package/data/packages/courier.json +0 -59
  13. package/data/packages/dnd.json +0 -77
  14. package/data/packages/familiar.json +0 -40
  15. package/data/packages/flux.json +0 -93
  16. package/data/packages/focus.json +0 -37
  17. package/data/packages/forge.json +0 -83
  18. package/data/packages/gesture.json +0 -25
  19. package/data/packages/herald.json +0 -108
  20. package/data/packages/illusionist.json +0 -132
  21. package/data/packages/keymap.json +0 -60
  22. package/data/packages/ledger.json +0 -57
  23. package/data/packages/lingua.json +0 -68
  24. package/data/packages/necromancer.json +0 -50
  25. package/data/packages/orbit.json +0 -99
  26. package/data/packages/ore.json +0 -68
  27. package/data/packages/postmaster.json +0 -51
  28. package/data/packages/prism.json +0 -66
  29. package/data/packages/pulse.json +0 -70
  30. package/data/packages/refine.json +0 -12
  31. package/data/packages/ripple.json +0 -83
  32. package/data/packages/rune.json +0 -79
  33. package/data/packages/sandbox.json +0 -40
  34. package/data/packages/scout.json +0 -61
  35. package/data/packages/scroll.json +0 -109
  36. package/data/packages/sentinel.json +0 -35
  37. package/data/packages/sourcerer.json +0 -73
  38. package/data/packages/spell.json +0 -133
  39. package/data/packages/tempo.json +0 -81
  40. package/data/packages/vault.json +0 -79
  41. package/data/packages/ward.json +0 -114
  42. package/data/packages/wayfinder.json +0 -110
  43. package/data/refine.json +0 -11847
  44. package/data/search.json +0 -1582
@@ -1,110 +0,0 @@
1
- {
2
- "apiSource": "export {\n WayfinderApiError,\n WayfinderDisposedError,\n WayfinderError,\n WayfinderRedirectLoopError,\n WayfinderRouteError,\n} from './errors';\nexport { createBrowserHistory, createMemoryHistory } from './history';\nexport { redirectTo } from './middleware';\nexport type { Router } from './router';\nexport { createRouter } from './router';\nexport type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';\n",
3
- "docs": {
4
- "index": "---\ntitle: Wayfinder — Client-side router for TypeScript\ndescription: Framework-agnostic client-side router with typed params, async data loading, middleware, leave guards, and View Transitions support.\npackage: wayfinder\ncategory: routing\nkeywords: [router, client-side, middleware, guards, navigation, history, spa, typed-routes]\nrelated: [ripple, ward, herald]\nexports: [createRouter, createBrowserHistory, createMemoryHistory, redirectTo, WayfinderError, WayfinderApiError, WayfinderDisposedError, WayfinderRedirectLoopError, WayfinderRouteError]\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"wayfinder\" />\n\n## Why Wayfinder?\n\nManaging navigation by hand means scattered `popstate` listeners, duplicated path checks, and no shared abstraction for loading data or blocking navigation. Wayfinder moves all of that into one declarative table.\n\n```ts\n// Before — manual navigation with popstate\nwindow.addEventListener('popstate', () => {\n const path = window.location.pathname;\n if (path === '/') renderHome();\n else if (path.startsWith('/dashboard')) renderDashboard();\n else renderNotFound();\n});\ndocument.querySelectorAll('a[data-route]').forEach((a) => {\n a.addEventListener('click', (e) => {\n e.preventDefault();\n history.pushState({}, '', (e.currentTarget as HTMLAnchorElement).href);\n dispatchEvent(new PopStateEvent('popstate'));\n });\n});\n\n// After — with Wayfinder\nimport { createRouter } from '@vielzeug/wayfinder';\n\nconst router = createRouter({\n routes: {\n home: { path: '/' },\n dashboard: { path: '/dashboard' },\n },\n notFound: { component: NotFoundPage },\n});\n\nrouter.subscribe((state) => {\n render(state.matches.at(-1)?.component);\n});\n```\n\n<div class=\"decision-callout\">\n\n**Use Wayfinder when** you need named navigation, route-level data loading with cancellation, middleware, or leave guards in a framework-agnostic setup.\n\n**Consider a framework's built-in router when** you are deep in a single framework ecosystem (React Router, Vue Router) and want first-class component binding with no adapter layer.\n\n</div>\n\n| Feature | Wayfinder | page.js | Navigo |\n| ------------------------------------ | ----------------------------------------------- | ------------------------------------------ | ------------------------------------------ |\n| Bundle size | <PackageInfo package=\"wayfinder\" type=\"size\" /> | ~1 kB | ~5 kB |\n| History mode | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Memory history (tests / non-browser) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Typed path params | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Named navigation | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Partial |\n| Middleware | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Data loaders with AbortSignal | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Lazy route loading | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Declarative redirects | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Search param validation | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Error in state | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| History state in context | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Leave guards | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Hover prefetching (`preload()`) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Scroll restoration | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| View Transition API | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Zero dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/wayfinder\n```\n\n```sh [npm]\nnpm install @vielzeug/wayfinder\n```\n\n```sh [yarn]\nyarn add @vielzeug/wayfinder\n```\n\n:::\n\n## Quick Start\n\nCreate a memory-backed router, wait for initial routing, then navigate by name.\n\n```ts\nimport { createMemoryHistory, createRouter } from '@vielzeug/wayfinder';\n\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n settings: { path: '/settings' },\n },\n});\n\nawait router.ready;\nawait router.navigate({ name: 'settings' });\nconsole.log(router.getSnapshot().location.pathname); // /settings\nrouter.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `createRouter()` — Compiles named, nested route tables.\n- `navigate()` — Commits route changes after middleware reaches its terminal stage.\n- `ready` — Signals that initial routing has settled.\n- `data()` — Receives cancellation through `AbortSignal` and can stream async-generator updates.\n- `beforeLeave()` — Blocks route exits before history changes.\n- `match()` / `load()` — Inspect routes synchronously or load route data without navigation.\n- `preload()` — Warms route data for a later matching navigation.\n- `createMemoryHistory()` — Runs routers in tests and non-browser environments.\n- `subscribe()` — Reactive subscription to navigation state changes.\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ripple](/ripple/) — reactive signals; sync router state to a signal for framework-agnostic reactivity\n- [Ward](/ward/) — permission guards; use inside Wayfinder middleware to protect routes\n- [Herald](/herald/) — event bus; dispatch route-change events to decouple navigation side effects\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
5
- "api": "---\ntitle: Wayfinder — API Reference\ndescription: Complete API reference for Wayfinder.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n|-----------------------------------------|------------------------------------------------------------|----------------------|-----------------------------------------------------------------------------------------------------------------|\n| `createRouter(options)` | Create a router from a route table | Sync | Initial navigation starts asynchronously in the constructor |\n| `createBrowserHistory()` | Create the default browser history driver | Sync | — |\n| `createMemoryHistory(initialPath?)` | Create an in-memory history driver | Sync | — |\n| `redirectTo(target, options?)` | Build redirect middleware | Sync (returns fn) | Does not call `next()` — always short-circuits the chain |\n| `router.navigate(target, options?)` | Navigate to a named route, raw path object, or string path | Async | No-op when destination equals current URL unless `force: true` |\n| `router.getSnapshot()` | Return the current immutable route state | Sync | Does not subscribe — call `subscribe()` to react to changes |\n| `router.subscribe(listener)` | Register a listener for state changes | Sync (returns unsub) | Listener is **not** called immediately with current state |\n| `router.url(name, params?, query?)` | Build a URL for a named route | Sync | Throws if the route name is unknown |\n| `router.isActive(name, options?)` | Check if a named route matches the current URL | Sync | Compares against the current snapshot pathname, not `history.location` directly |\n| `router.match(pathname)` | Inspect a pathname as a branch without side effects | Sync | Returns `null` for redirect routes |\n| `router.load(url, options?)` | Load a URL into a full state including data loaders | Async | Middleware is not executed; lazy modules are resolved as a side effect |\n| `router.ready` | Await the initial navigation | Async | Rejects when initial loading fails |\n| `router.preload(name, params?, query?)` | Eagerly run data loaders without navigating | Async | Pass `query` to match the navigation cache key; rejects with `WayfinderDisposedError` if the router is disposed |\n| `router.waitFor(name)` | Wait for the router to settle on a named route | Async | Rejects immediately if `status === 'error'`; rejects with `WayfinderDisposedError` if disposed while pending |\n| `router.beforeLeave(blocker, options?)` | Register a global leave guard | Sync (returns unsub) | Scoped to specific routes via `options.routes` |\n| `router.dispose()` | Remove listeners and shut down the router | Sync | Idempotent — safe to call multiple times |\n\n## Package Entry Points\n\n| Import | Purpose |\n| --------------------- | ---------------------- |\n| `@vielzeug/wayfinder` | Main exports and types |\n\n## `createRouter(options)`\n\n```ts\nimport { createRouter } from '@vielzeug/wayfinder';\n\nconst router = createRouter({\n base: '/app',\n routes: {\n home: { path: '/' },\n dashboard: {\n path: '/dashboard',\n children: {\n index: { index: true },\n settings: { path: 'settings', data: () => fetchSettings() },\n },\n },\n },\n notFound: { component: NotFoundPage },\n});\n```\n\n| Option | Type | Default | Description |\n| ---------------- | ----------------------------------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `base` | `string` | `'/'` | Base path prefix for all routes |\n| `coerceSearch` | `CoerceSearchFn` | — | Global search-param coercion applied to every route that does not define its own `coerceSearch`. Throwing falls back to raw strings and is reported via `onError`. |\n| `history` | `HistoryDriver` | `createBrowserHistory()` | History source used for reading locations and writing navigations |\n| `middleware` | `Middleware[]` | `[]` | Global middleware prepended to every route |\n| `notFound` | `{ component?, data?, meta?, middleware? }` | — | Synthetic route used when no path matches. Global middleware runs first, then `notFound.middleware` and `notFound.data`. `ctx.pathname` is the unmatched path. |\n| `onError` | `(error, context: RouterErrorContext) => void` | — | Optional sink for non-awaited/background router errors |\n| `routes` | `RouteTable` | required | Declarative route table. Object key order defines match precedence. |\n| `scroll` | `(to, from) => ScrollDecision` | — | Called after each navigation. Return `'top'` to scroll to top, `'preserve'` to keep the current position, or `{ x, y }` for a specific position. |\n| `viewTransition` | `boolean` | `false` | Wrap navigations in the View Transition API when available |\n\n**Returns:** `Router`\n\n## Route Table\n\nDefine routes as a plain object where keys become route names. TypeScript will infer route params from literal `path` strings.\n\n```ts\nconst routes = {\n home: { path: '/' },\n userDetail: { path: '/users/:id' },\n files: { path: '/files/:rest*' },\n};\n```\n\nNested routes are declared with `children`, and child names become compound names with dot notation.\n\n## Route Definition\n\n```ts\nconst routes = {\n home: { path: '/' },\n dashboard: {\n path: '/dashboard',\n middleware: [requireAuth],\n children: {\n index: { index: true },\n settings: {\n path: 'settings',\n data: async () => fetchSettings(),\n },\n },\n },\n userDetail: {\n path: '/users/:id',\n meta: { section: 'users' },\n data: async ({ params }) => fetchUser(params.id),\n onError: (error) => ({ error, user: null }),\n },\n};\n```\n\nEach route definition supports these fields:\n\n| Field | Type | Description |\n| -------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| `path` | `string` | Wayfinder pattern. Supports static paths, `:param`, `:param*`, and `*`. Child paths are relative unless they start with `/`. |\n| `children` | `Record<string, RouteDefinition>` | Nested child routes. Child names are appended to the parent route name. |\n| `index` | `boolean` | Default child route that inherits the parent path. |\n| `component` | `unknown` | Optional framework view payload exposed on the leaf `RouteMatch`. |\n| `data` | `DataFn` | Data loader. Runs after middleware; result available as `match.data`. Supports streaming via `AsyncGenerator`. |\n| `lazy` | `() => Promise<{ data?, component?, meta? }>` | Lazy-load the route module. Called once on first navigation; result overrides static fields in the hydration cache. |\n| `meta` | `unknown` | Static metadata exposed on each `RouteMatch` in the branch. |\n| `middleware` | `Middleware[]` | Optional route-specific middleware |\n| `onError` | `(error, context: DataContext) => MaybePromise<unknown>` | Per-route error boundary for data loader failures. Return value becomes `match.data` for degraded rendering. |\n| `redirect` | `NavigationTarget` | Declarative redirect. Resolved before middleware runs; uses `replaceState` so the original URL is never added to history. |\n| `coerceSearch` | `(raw: QueryParams) => ResolvedQueryParams` | Coerce raw URL string values into typed values. Return value replaces `ctx.query`. Throwing leaves the parsed query unchanged. |\n\n## `createBrowserHistory()`\n\n```ts\nimport { createBrowserHistory } from '@vielzeug/wayfinder';\n\nconst history = createBrowserHistory();\n```\n\nCreate the default `HistoryDriver` backed by the browser History API.\n\n## `createMemoryHistory(initialPath?)`\n\n```ts\nimport { createMemoryHistory } from '@vielzeug/wayfinder';\n\n// Tests\nconst router = createRouter({\n history: createMemoryHistory('/dashboard'),\n routes,\n});\n\n// Controlled non-browser runtime\nconst router = createRouter({\n history: createMemoryHistory('/request-path'),\n routes,\n});\n```\n\nCreate an in-memory `HistoryDriver`. No browser history globals required — suitable for unit tests and controlled non-browser runtimes. The optional `initialPath` defaults to `'/'`.\n\n## `Router`\n\n### Lifecycle\n\n#### `router.dispose()`\n\nRemove listeners, clear subscribers, and reject future router interaction. Idempotent — safe to call multiple times.\n\n**Returns:** `void`\n\n**Throws:** Never.\n\n---\n\n#### `router.disposed`\n\n`boolean` — `true` after `dispose()` has been called.\n\n---\n\n#### `router.disposalSignal`\n\n`AbortSignal` that is aborted (with a `WayfinderDisposedError` reason) when the router is disposed. Use this to tie external resource lifetimes to the router's lifetime.\n\n```ts\nsource.on('update', syncRouteParams, { signal: router.disposalSignal });\n```\n\n---\n\n### Navigation\n\n#### `router.navigate(target, options?)`\n\n```ts\nawait router.navigate({ name: 'userDetail', params: { id: '42' } });\nawait router.navigate({ name: 'userDetail', params: { id: '42' } }, { replace: true });\nawait router.navigate({ name: 'search', query: { q: 'wayfinder' }, hash: 'results' });\n```\n\n| Option | Type | Default | Description |\n| ---------------- | --------- | ------- | ------------------------------------------------------- |\n| `replace` | `boolean` | `false` | Use `replaceState` instead of `pushState` |\n| `state` | `unknown` | — | History state payload |\n| `viewTransition` | `boolean` | — | Override the router-level setting for this navigation |\n| `force` | `boolean` | `false` | Re-run even when the destination URL is already current |\n\n**Returns:** `Promise<void>`\n\nHistory is written only after middleware reaches the terminal stage. Returning from middleware without `next()` cancels the programmatic navigation without changing history or the route snapshot.\n\nNamed routes stay the primary API, but `navigate()` also accepts raw path objects or a plain string:\n\n```ts\nawait router.navigate({ path: '/marketing?utm_source=campaign' });\nawait router.navigate({ path: '/checkout#payment' }, { replace: true });\n\n// Plain string — most concise for direct paths\nawait router.navigate('/about');\nawait router.navigate('/search?q=hello');\n```\n\n---\n\n### Route Helpers\n\n#### `router.url(name, params?, query?)`\n\n```ts\nrouter.url('userDetail', { id: '42' });\nrouter.url('userDetail', { id: '42' }, { tab: 'profile' });\n```\n\nBuild a base-aware URL for a named route.\n\n**Returns:** `string`\n\n#### `router.isActive(name, options?)`\n\n```ts\nrouter.isActive('userDetail');\nrouter.isActive('users');\nrouter.isActive('users', { exact: true });\n```\n\nCheck whether the current pathname matches a named route exactly or by prefix.\n\n**Returns:** `boolean`\n\n#### `router.match(pathname)`\n\n```ts\nrouter.match('/app/dashboard/settings');\n// => [\n// { name: 'dashboard', ... },\n// { name: 'dashboard.settings', ... },\n// ]\n```\n\nInspect a pathname without running middleware, data loaders, or subscribers. Strips the configured `base` automatically. Returns the matched branch from root to leaf, or `null` for redirect routes and no-match.\n\n**Returns:** `RouteMatchBranch | null`\n\n---\n\n#### `router.load(url, options?)`\n\n```ts\n// SSR data prefetch\nconst state = await router.load('/users/42');\n\n// With cancellation\nconst controller = new AbortController();\nconst state = await router.load('/dashboard', { signal: controller.signal });\n```\n\nLoad a full URL into a `RouteState` including data loader results, without modifying router state or history. Follows declarative redirects (up to five hops) and resolves lazy modules as a side effect. Returns `null` for unmatched URLs.\n\nMiddleware is **not** executed — `load` is a data-only prefetch for SSR and pre-rendering where middleware side effects are not wanted. If your data loaders depend on `ctx.locals` set by middleware, use `navigate()` instead.\n\nWhen a `data()` function throws, the returned state has `status: 'error'` and `error` set to the thrown value.\n\n**Returns:** `Promise<RouteState | null>`\n\n---\n\n#### `router.waitFor(name)`\n\n```ts\n// Navigate and wait for data to settle\nawait router.navigate({ name: 'userDetail', params: { id: '42' } });\nconst state = await router.waitFor('userDetail');\nconst user = state.matches.at(-1)?.data;\n\n// Useful in tests with memory history:\nconst history = createMemoryHistory('/dashboard');\nconst router = createRouter({ history, routes });\nconst state = await router.waitFor('dashboard');\n```\n\nWaits for the router to reach `status: 'idle'` with the named route active in the matched branch. Rejects immediately if `status === 'error'`. Resolves immediately if the router is already idle on the target route. Also rejects if `router.dispose()` is called while the promise is pending.\n\n> **Note:** `waitFor` skips intermediate `'streaming'` states — it only resolves once the status reaches `'idle'`. It does not resolve while the route is still streaming partial data.\n\n**Returns:** `Promise<RouteState>`\n\n---\n\n#### `router.preload(name, params?, query?)`\n\n```ts\n// Hover-prefetch without query\nanchor.addEventListener('mouseenter', () => {\n router.preload('userDetail', { id: '42' });\n});\n\n// Hover-prefetch with matching query to avoid a cache miss\nanchor.addEventListener('mouseenter', () => {\n router.preload('search', undefined, { q: 'hello' });\n});\n```\n\nEagerly runs the data loaders for a named route without navigating. Useful for hover-prefetch. Concurrent calls for the same `name + params + query` combination are deduplicated. Results are consumed on the next navigation to the same route with the same cache key.\n\nPass the same `query` you intend to navigate with to ensure the preloaded result hits the cache. Without `query`, the key is the bare path — a navigation with a query string will produce a cache miss and re-run the loader.\n\nIn-flight preloads are aborted automatically via the router's disposal signal when `router.dispose()` is called. Calling `preload()` on an already-disposed router throws `WayfinderDisposedError` immediately, without running the data loader — consistent with `navigate()`, `subscribe()`, `beforeLeave()`, and `waitFor()`.\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `router.beforeLeave(blocker, options?)`\n\n```ts\n// Guard unsaved-changes forms\nconst remove = router.beforeLeave(async (destination) => {\n if (!form.isDirty) return true;\n return confirm(`Leave without saving? (going to ${destination.pathname})`);\n});\n\n// Remove the guard when the form unmounts\nremove();\n```\n\nRegister a global leave guard called before user-triggered navigation attempts. Return `true` to allow, `false` to cancel. Multiple guards can be registered; navigation is blocked if any guard returns `false`.\n\nScope a guard to fire only when leaving specific routes using the `routes` option:\n\n```ts\nrouter.beforeLeave(async () => confirm('Discard changes?'), { routes: ['editor'] });\n```\n\nThe guard fires when the router is leaving any route whose name appears in the `routes` array (any node in the active branch, not just the leaf). Declarative `redirect` routes bypass all leave guards.\n\n**Returns:** `() => void`\n\n## `redirectTo(target, options?)`\n\n```ts\nimport { redirectTo } from '@vielzeug/wayfinder';\n\nconst requireAuth = redirectTo({ name: 'login' }, { replace: true });\n```\n\nCreates middleware that navigates to `target` and short-circuits the middleware chain (does not call `next()`). Useful for auth guards and route aliases in middleware.\n\nFor permanent declarative redirects (URL aliases), use the `redirect` field on the route definition instead.\n\n> **Note:** `redirectTo()` internally calls `ctx.navigate()`, which runs `beforeLeave` guards. If a guard blocks navigation, the redirect will not complete. Declarative `redirect` on a route definition bypasses guards entirely.\n\n**Returns:** `Middleware`\n\n---\n\n### State\n\n#### `router.ready`\n\nA `Promise<void>` for the constructor-triggered navigation. It resolves after initial middleware, redirects, lazy modules, and data loaders settle. It resolves after a blocked or unmatched initial navigation, and rejects if initial navigation fails.\n\n```ts\nconst router = createRouter({ routes });\nawait router.ready;\n```\n\n---\n\n#### `router.getSnapshot()`\n\nReturns the current immutable route state snapshot. Use this to read state synchronously. Compatible with React's `useSyncExternalStore`:\n\n```ts\nconst state = useSyncExternalStore(\n (cb) => router.subscribe(cb),\n () => router.getSnapshot(),\n);\n```\n\n```ts\nconst { location, matches, status, error } = router.getSnapshot();\n\nlocation.pathname;\nlocation.query; // raw parsed query (QueryParams) — always string values\nlocation.hash;\nlocation.historyState; // value passed to navigate({ ... }, { state: ... })\n\n// When status === 'error':\nconsole.error(error);\n```\n\n`error` is only set when `status === 'error'`. It holds the exact value thrown by the failing `data()` function.\n\n**Returns:** `RouteState`\n\n#### `router.subscribe(listener)`\n\n```ts\nconst unsubscribe = router.subscribe((state) => {\n const leaf = state.matches.at(-1);\n document.title = (leaf?.meta as { title?: string } | undefined)?.title ?? 'App';\n});\n```\n\nRegister a listener for future state changes, including loading and streaming updates. The listener is **not** called with the current snapshot — call `router.getSnapshot()` when you subscribe if you need it.\n\n**Returns:** `() => void`\n\n## Types\n\n### `RouteContext<Params, TRoutes>`\n\nContext passed to middleware and data loader functions.\n\n```ts\ntype RouteContext<Params extends RouteParams = RouteParams, TRoutes extends RouteTable = RouteTable> = {\n readonly hash: string;\n /** State stored on the history entry that triggered this navigation. */\n readonly historyState: unknown;\n locals: Record<string, unknown>;\n readonly matches: RouteMatchBranch;\n readonly navigate: (\n target: NamedNavigationTarget<TRoutes> | RawNavigationTarget | string,\n options?: NavigateOptions,\n ) => Promise<void>;\n readonly params: Params;\n readonly pathname: string;\n readonly query: ResolvedQueryParams;\n};\n```\n\nRead route metadata from the leaf match: `ctx.matches.at(-1)?.meta`.\n\n`ctx.locals` is mutable and shared across the entire middleware chain for one navigation. Use it to pass values from middleware to data loaders.\n\n`ctx.query` is the coerced query (after `coerceSearch`). `router.getSnapshot().location.query` always contains raw string values from URL parsing.\n\n### `DataFn<Params, TRoutes>`\n\n```ts\ntype DataFn<Params extends RouteParams = RouteParams, TRoutes extends RouteTable = RouteTable> = (\n context: DataContext<Params, TRoutes>,\n) => DataStream | MaybePromise<unknown>;\n```\n\nReturn an `AsyncGenerator` to stream partial results (see `DataStream`).\n\n### `DataContext<Params, TRoutes>`\n\n```ts\ntype DataContext<Params extends RouteParams = RouteParams, TRoutes extends RouteTable = RouteTable> = RouteContext<\n Params,\n TRoutes\n> & {\n readonly signal: AbortSignal;\n};\n```\n\n### `DataStream<T>`\n\n```ts\ntype DataStream<T = unknown> = AsyncGenerator<T, T>;\n```\n\nReturn a `DataStream` from a `data()` function to stream partial results. Each `yield` updates `match.data` immediately with `match.status: 'streaming'`. The `return` value is the final settled data with `match.status: 'idle'`.\n\n```ts\ndata: async function* ({ signal }) {\n const items: Item[] = [];\n for await (const batch of streamBatches({ signal })) {\n items.push(...batch);\n yield items; // partial — status: 'streaming'\n }\n return items; // final — status: 'idle'\n},\n```\n\n### `Middleware<TRoutes>`\n\n```ts\ntype Middleware<TRoutes extends RouteTable = RouteTable> = (\n context: RouteContext<RouteParams, TRoutes>,\n next: () => Promise<void>,\n) => void | Promise<void>;\n```\n\nMiddleware ordering is simple: global middleware first, then route middleware, then `data()`.\n\n### `UntypedNamedNavigationTarget`\n\n```ts\ntype UntypedNamedNavigationTarget = {\n hash?: string;\n name: string;\n params?: RouteParams;\n query?: ResolvedQueryParams;\n};\n```\n\n### `NavigationTarget`\n\n```ts\ntype NavigationTarget =\n | {\n path: string;\n }\n | {\n hash?: string;\n name: string;\n params?: RouteParams;\n query?: ResolvedQueryParams;\n };\n```\n\n### `NavigateOptions`\n\n```ts\ntype NavigateOptions = {\n force?: boolean;\n replace?: boolean;\n state?: unknown;\n viewTransition?: boolean;\n};\n```\n\n### `RouteState`\n\n```ts\ntype RouteState = {\n /** The value thrown by a `data()` function. Only set when `status === 'error'`. */\n readonly error?: unknown;\n readonly location: RouteLocation;\n readonly matches: readonly RouteMatch[];\n readonly status: NavigationStatus;\n};\n\ntype RouteLocation = {\n readonly hash: string;\n /** State stored on the history entry that triggered this navigation. */\n readonly historyState: unknown;\n readonly pathname: string;\n /** Raw parsed query params — always string values from URL parsing.\n * For coerced values (numbers, booleans), read `ctx.query` inside middleware or data loaders.\n */\n readonly query: QueryParams;\n};\n```\n\n### `RouteMatch`\n\n```ts\ntype RouteMatch = {\n readonly component: unknown;\n readonly data: unknown;\n readonly meta: unknown;\n readonly name: string;\n readonly params: RouteParams;\n readonly pathname: string;\n /** Per-node loading status. Reflects individual loader state in nested layouts. */\n readonly status: NavigationStatus;\n};\n```\n\n### `RouteMatchBranch`\n\n```ts\ntype RouteMatchBranch = readonly RouteMatch[];\n```\n\n### `PathParams<T>`\n\n```ts\ntype UserParams = PathParams<'/users/:id'>;\n// => { readonly id: string }\n\ntype FileParams = PathParams<'/files/:rest*'>;\n// => { readonly rest: string }\n```\n\n### `QueryParams`\n\n```ts\ntype QueryParams = Record<string, string | string[]>;\n```\n\nRepresents parsed URL query values before route-level coercion.\n\n### `ResolvedQueryParams`\n\n```ts\ntype ResolvedQueryValue = string | number | boolean;\ntype ResolvedQueryParams = Record<string, ResolvedQueryValue | ResolvedQueryValue[]>;\n```\n\nRepresents the query object after optional `coerceSearch` normalization.\n\n### `NavigationStatus`\n\n```ts\ntype NavigationStatus = 'idle' | 'loading' | 'streaming' | 'error';\n```\n\nTop-level status of the router. `'streaming'` means at least one active data loader is an async generator and has yielded at least one value but has not yet returned.\n\nEach `RouteMatch` also carries a `status: NavigationStatus` for per-node loading state in nested layouts.\n\n### `RouteMiddleware<Path, TRoutes>`\n\n```ts\ntype RouteMiddleware<Path extends string = string, TRoutes extends RouteTable = RouteTable> = (\n context: RouteContext<PathParams<Path>, TRoutes>,\n next: () => Promise<void>,\n) => void | Promise<void>;\n```\n\nTyped variant of `Middleware` scoped to a route path. Provides typed `ctx.params` matching the path pattern.\n\n```ts\nconst guard: RouteMiddleware<'/users/:id'> = (ctx, next) => {\n console.log(ctx.params.id); // string\n return next();\n};\n```\n\n### `CoerceSearchFn<Q>`\n\n```ts\ntype CoerceSearchFn<Q extends ResolvedQueryParams = ResolvedQueryParams> = (\n raw: QueryParams,\n) => Q;\n```\n\nFunction signature for both the per-route `coerceSearch` field and the global `RouterOptions.coerceSearch` option. Receives raw URL strings and returns typed values. Throwing inside the function falls back to the original raw query.\n\n### `BeforeLeaveOptions<TRoutes>`\n\n```ts\ntype BeforeLeaveOptions<TRoutes extends RouteTable = RouteTable> = {\n /** Route names that trigger this guard. Omit for a global guard. */\n routes?: RouteName<TRoutes>[];\n};\n```\n\nPassed as the second argument to `router.beforeLeave()`. When `routes` is provided, the guard only fires when the router leaves a route whose name is in the array.\n\n### `BeforeLeaveBlocker`\n\n```ts\n// Return true to allow navigation, false to cancel.\ntype BeforeLeaveBlocker = (destination: NavigationDestination) => MaybePromise<boolean>;\n```\n\n### `NavigationDestination`\n\n```ts\ntype NavigationDestination = {\n readonly name?: string; // route name if navigating to a named route\n readonly params: RouteParams;\n readonly pathname: string;\n readonly query: QueryParams;\n};\n```\n\nPassed to every `beforeLeave` blocker. Use `destination.pathname` and `destination.query` to make context-aware allow/block decisions.\n\n### `IsActiveOptions`\n\n```ts\ntype IsActiveOptions = {\n /** Require an exact pathname match. Defaults to prefix matching. */\n exact?: boolean;\n};\n```\n\n### `ScrollDecision`\n\n```ts\ntype ScrollPosition = { x: number; y: number };\ntype ScrollDecision = ScrollPosition | 'preserve' | 'top';\n```\n\n### `RouterErrorContext`\n\n```ts\ntype RouterErrorContext =\n | { routeName: string; source: 'data-loader' } // data() threw\n | { routeName: string; source: 'middleware' } // middleware threw\n | { source: 'coerce-search' | 'history-listener' | 'initial-navigation' | 'preload' };\n```\n\nPassed to the `onError` callback in `createRouter` options. The `routeName` is present when the error originates from a named route's `data()` or `middleware`.\n\n### `HistoryDriver`\n\n```ts\ninterface HistoryDriver {\n readonly location: {\n readonly hash: string;\n readonly pathname: string;\n readonly search: string;\n readonly state: unknown;\n };\n /** Navigate one entry back in history, equivalent to the browser back button. */\n back(): void;\n push(url: string, state?: unknown): void;\n replace(url: string, state?: unknown): void;\n /**\n * Subscribe to backwards/forwards navigation (popstate-equivalent).\n * `push()` and `replace()` are silent — they do not notify subscribers.\n * Only `back()` (and browser popstate events) trigger notifications.\n * Returns an unsubscribe function.\n */\n onPopstate(listener: () => void): () => void;\n}\n```\n\n### `RouteDefinition<Path>`\n\n```ts\ntype RouteDefinition<Path extends string = string> =\n | ContentRouteDefinition<Path> // path + data/component/meta/middleware/coerceSearch/lazy/onError\n | RedirectRouteDefinition<Path>; // path + redirect\n```\n\nThe union type for a single entry in the route table. Use this to type externally-defined route objects:\n\n```ts\nimport type { RouteDefinition } from '@vielzeug/wayfinder';\n\nconst userDetail: RouteDefinition<'/users/:id'> = {\n path: '/users/:id',\n data: async ({ params }) => fetchUser(params.id),\n};\n```\n\n### `RouterOptions<TRoutes>`\n\nThe options object accepted by `createRouter()`. See the [`createRouter(options)`](#createrouter-options) options table above for the full field reference.\n\n```ts\nimport type { RouterOptions } from '@vielzeug/wayfinder';\n\nconst options: RouterOptions<typeof routes> = {\n routes,\n base: '/app',\n};\n```\n\n### `Unsubscribe`\n\n```ts\ntype Unsubscribe = () => void;\n```\n\n## Errors\n\n### `WayfinderError`\n\nBase class for every error Wayfinder throws. Catch this to handle any router-originated error without enumerating subclasses.\n\n```ts\nimport { WayfinderError } from '@vielzeug/wayfinder';\n\ntry {\n await router.navigate({ name: 'home' });\n} catch (e) {\n if (e instanceof WayfinderError) {\n // any router-originated error — check e.name or `instanceof` a subclass for detail\n }\n}\n```\n\n### `WayfinderDisposedError`\n\nThrown when `navigate()`, `subscribe()`, `beforeLeave()`, `waitFor()`, or `preload()` is called after `dispose()`. Also used as the `AbortSignal.reason` on `disposalSignal`.\n\n```ts\nimport { WayfinderDisposedError } from '@vielzeug/wayfinder';\n\ntry {\n await router.navigate({ name: 'home' });\n} catch (e) {\n if (e instanceof WayfinderDisposedError) {\n // router was disposed\n }\n}\n```\n\n### `WayfinderRouteError`\n\nThrown for malformed route definitions — at `createRouter()` time for config errors, or when a `url()`/`navigate()` call references an unknown route name or a missing path param.\n\n### `WayfinderRedirectLoopError`\n\nThrown when a chain of declarative `redirect`s (or a mix of declarative redirects and `ctx.navigate()` calls inside route middleware) exceeds 5 hops.\n\n### `WayfinderApiError`\n\nThrown on middleware misuse — currently only when a middleware function calls its `next()` more than once.\n\n### Runtime error messages\n\n| Message | Class | When |\n| ----------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------- |\n| `Router is disposed` | `WayfinderDisposedError` | Calling a guarded method (see above) after `dispose()` |\n| `Unknown route name: X. Available routes: Y` | `WayfinderRouteError` | Navigating to, resolving, or building a URL for an unregistered route |\n| `Route \"X\" cannot define both index and path` | `WayfinderRouteError` | A route sets `index: true` and `path` at the same time |\n| `Route \"X\" must define path or set index: true` | `WayfinderRouteError` | A route defines neither `index: true` nor `path` |\n| `Duplicate route name: \"X\"` | `WayfinderRouteError` | Two routes resolve to the same compound name during `createRouter()` |\n| `Missing path param: X` | `WayfinderRouteError` | `url()`/`navigate()`/`preload()` omits a param the path pattern requires |\n| `Invalid param name \":X\" in path \"Y\"` | `WayfinderRouteError` | A param name contains non-word characters (e.g., `:user-id`) |\n| `Wildcard \"*\" must be the final segment in path: X` | `WayfinderRouteError` | A `*` segment appears before the last segment |\n| `Wildcard param must be final segment in path: X` | `WayfinderRouteError` | A `:param*` greedy param appears before the last segment |\n| `Redirect loop detected` | `WayfinderRedirectLoopError` | A declarative `redirect` chain (or mixed redirect + `navigate()`) exceeds 5 hops |\n| `next() called multiple times` | `WayfinderApiError` | Middleware calls its `next()` callback more than once |\n\n## Pattern Rules\n\n| Pattern | Example | Meaning |\n| ------------------------------ | ------------------- | ------------------------------------------- |\n| `/about` | `/about` | Exact static path |\n| `/users/:id` | `/users/42` | Single named param |\n| `/users/:userId/posts/:postId` | `/users/1/posts/2` | Multiple named params |\n| `/docs/*` | `/docs/guide/intro` | Wildcard suffix without a named capture |\n| `/files/:rest*` | `/files/a/b/c` | Wildcard suffix captured as one named param |\n| `*` | anything | Global catch-all |\n\n## Design Notes\n\n- Wayfinder no longer exposes imperative registration methods like `on()`, `group()`, or `use()`.\n- Wayfinder names come from the route-table object keys.\n- `data()` is the terminal action. Its return value becomes `match.data`. There is no separate `handler` step.\n- For unmatched URLs, use the `notFound` router option rather than `path: '*'` in the route table.\n- Error handling is middleware that wraps `await next()`. The thrown error is also stored on `router.getSnapshot().error`.\n- Declarative `redirect` on a route definition is for permanent alias redirects. The `redirectTo()` middleware helper is for conditional guards.\n- `lazy` factories are called at most once per `RouteRecord`. The loaded `data`/`component`/`meta` are stored in the router's internal hydration cache. `handler` is not accepted in the lazy-resolved module.\n- `onError` in a route definition is a per-route data-loader boundary. If `onError` itself throws, the router falls through to `status: 'error'` as usual.\n",
6
- "usage": "---\ntitle: Wayfinder — Usage Guide\ndescription: Router setup, middleware, data loading, nested routes, and state patterns for Wayfinder.\n---\n\n[[toc]]\n\n::: tip New to Wayfinder?\nStart with the [Overview](./index.md), then use this page for the day-to-day API.\n:::\n\n## Basic Usage\n\nCreate a deterministic router with memory history, wait for startup, and navigate by route name.\n\n```ts\nimport { createMemoryHistory, createRouter } from '@vielzeug/wayfinder';\n\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n settings: {\n data: async () => ({ section: 'settings' }),\n path: '/settings',\n },\n },\n});\n\nawait router.ready;\nawait router.navigate({ name: 'settings' });\nconsole.log(router.getSnapshot().matches.at(-1)?.data);\nrouter.dispose();\n```\n\n`routes` is required. Route keys become names, and object key order controls match precedence.\n\n## Define Routes\n\nEach route can provide these fields:\n\n| Field | Purpose |\n| -------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| `path` | Match pattern |\n| `children` | Nested child routes |\n| `index` | Default child route that inherits the parent path |\n| `component` | Optional view payload exposed on `match.component` |\n| `data` | Abortable route data function. Result available as `match.data`. Supports streaming via `AsyncGenerator`. |\n| `lazy` | Lazy-load the module. Called once; result fills `data`, `component`, and `meta`. |\n| `meta` | Static metadata exposed on `match.meta` |\n| `middleware` | Route-specific middleware |\n| `onError` | Per-route error boundary. Called when this route's `data()` throws; its return value becomes `match.data`. |\n| `redirect` | Declarative permanent redirect. Resolved before middleware runs. |\n| `coerceSearch` | Coerce raw URL search strings into typed values. Return value replaces `ctx.query`. Throw to leave the raw query unchanged. |\n\nUse wildcard routes for fallback behavior:\n\n```ts\nconst routes = {\n docs: { path: '/docs/*' },\n};\n```\n\nFor a catch-all not-found page, use the `notFound` option in router options instead of a `path: '*'` route:\n\n```ts\nconst router = createRouter({\n routes,\n notFound: {\n component: NotFoundPage,\n data: async ({ pathname }) => ({ requestedPath: pathname }),\n },\n});\n```\n\nAlternatively, `path: '*'` still works as a named route when you need to navigate to it explicitly.\n\nNested routes compose naturally and create compound route names:\n\n```ts\nconst routes = {\n dashboard: {\n path: '/dashboard',\n children: {\n index: { index: true },\n settings: { path: 'settings' },\n },\n },\n};\n\nawait router.navigate({ name: 'dashboard.settings' });\n```\n\n## Route Context\n\nMiddleware and data loaders receive a `RouteContext`:\n\n```ts\nuserDetail: {\n path: '/users/:id',\n middleware: [\n (ctx, next) => {\n ctx.params.id; // typed to path params\n ctx.query.tab; // resolved query (after coerceSearch)\n ctx.pathname;\n ctx.hash;\n ctx.historyState; // value from navigate({ ... }, { state: ... })\n ctx.locals; // mutable bag shared across the middleware chain\n ctx.navigate; // programmatic navigation\n return next();\n },\n ],\n data: async (ctx) => {\n ctx.signal; // AbortSignal — cancelled when navigation is superseded\n return fetchUser(ctx.params.id, { signal: ctx.signal });\n },\n}\n```\n\n`ctx.locals` is mutable and shared through the entire middleware chain for one navigation. Use it to pass values from middleware to data loaders.\n\n## Middleware\n\nMiddleware wraps the navigation using the familiar `async (ctx, next) => { ... }` shape.\n\n```ts\nconst requireAuth = redirectTo({ name: 'login' }, { replace: true });\n\nconst loadCurrentUser = async (ctx, next) => {\n ctx.locals.user = await fetchCurrentUser();\n await next();\n};\n```\n\nOrder is fixed and simple:\n\n```text\nglobal middleware\n ↓\nroute middleware\n ↓\ndata()\n```\n\n### Guards\n\nUse middleware for auth checks, redirects, analytics, and boundaries.\n\n```ts\nconst requireAuth = async (ctx, next) => {\n if (!session.currentUser) {\n await ctx.navigate({ name: 'login' }, { replace: true });\n return; // do not call next()\n }\n ctx.locals.user = session.currentUser;\n await next();\n};\n```\n\nFor unconditional redirects, use the `redirectTo()` helper:\n\n```ts\nimport { redirectTo } from '@vielzeug/wayfinder';\n\nconst requireAuth = redirectTo({ name: 'login' }, { replace: true });\n```\n\nFor permanent URL aliases, use the declarative `redirect` field instead of middleware:\n\n```ts\nconst routes = {\n profile: { path: '/profile', redirect: { name: 'userDetail' } },\n userDetail: { path: '/users/:id' },\n};\n```\n\n> **Note:** `redirectTo()` calls `ctx.navigate()` internally, so `beforeLeave` guards will run and can block it. Declarative `redirect` on a route definition bypasses all leave guards.\n\n### Leave Guards\n\nRegister a global leave guard with `router.beforeLeave()`. Return `false` to cancel navigation.\n\n```ts\nconst removeGuard = router.beforeLeave(async (destination) => {\n if (!form.isDirty) return true;\n return confirm(`Discard changes? (navigating to ${destination.pathname})`);\n});\n\n// Remove when no longer needed:\nremoveGuard();\n```\n\nScope a guard to fire only when leaving specific routes:\n\n```ts\nrouter.beforeLeave(async () => confirm('Discard changes?'), { routes: ['editor'] });\n```\n\nDeclarative `redirect` routes bypass all leave guards.\n\n### Data Loading\n\nUse `data()` for route-local data acquisition. It receives the same route context plus an `AbortSignal`.\n\n```ts\nconst routes = {\n userDetail: {\n path: '/users/:id',\n data: async ({ params, signal }) => fetchUser(params.id, { signal }),\n },\n};\n```\n\nAccess the result via the matched branch:\n\n```ts\nrouter.subscribe((state) => {\n const user = state.matches.at(-1)?.data;\n renderUser(user);\n});\n```\n\n#### Per-route Error Boundaries\n\nUse `onError` to handle data loader failures per-route. The returned value becomes `match.data`, allowing the route to render a degraded state:\n\n```ts\nconst routes = {\n userDetail: {\n path: '/users/:id',\n data: async ({ params, signal }) => fetchUser(params.id, { signal }),\n onError: (error) => ({ error, user: null }),\n },\n};\n```\n\nIf `onError` itself throws, the router falls through to `status: 'error'` as usual.\n\n#### Streaming Data Loaders\n\nReturn an `AsyncGenerator` from `data()` to stream partial results. Each `yield` updates `match.status` to `'streaming'` and `match.data` to the yielded value. The `return` value is the final settled data.\n\n```ts\nconst routes = {\n feed: {\n path: '/feed',\n data: async function* ({ signal }) {\n const items: FeedItem[] = [];\n for await (const batch of streamFeedBatches({ signal })) {\n items.push(...batch);\n yield items; // stream partial results\n }\n return items; // final settled value\n },\n },\n};\n```\n\nDuring streaming, `state.status` is `'streaming'` and each `match.status` reflects the loading state of that individual branch node.\n\n### Lazy Routes\n\nDefer loading a route module until first navigation. The factory is called at most once.\n\n```ts\nconst routes = {\n settings: {\n path: '/settings',\n lazy: () => import('./pages/Settings'),\n },\n};\n```\n\nThe resolved object may contain `data`, `component`, and/or `meta`. Any present field overwrites the static definition.\n\n### Search Param Validation\n\nValidate and coerce `ctx.query` per route. The function receives raw URL strings (`QueryParams`). Throw to leave the parsed query unchanged.\n\n```ts\nconst routes = {\n search: {\n path: '/search',\n coerceSearch: (raw) => ({\n q: String(raw.q ?? ''),\n page: Math.max(1, Number(raw.page ?? 1)),\n }),\n data: async ({ query }) => searchPosts(query.q, query.page),\n },\n};\n```\n\nTo apply the same coercion to every route, set `coerceSearch` on the router options instead. Per-route `coerceSearch` takes precedence over the global one.\n\n```ts\nconst router = createRouter({\n coerceSearch: (raw) => ({ page: Number(raw.page ?? 1) }),\n routes,\n});\n```\n\n### Error Boundaries\n\nWrap `await next()` in middleware for route-wide error handling. The thrown error is also stored on `router.getSnapshot().error`.\n\n```ts\nconst boundary = async (ctx, next) => {\n try {\n await next();\n } catch (error) {\n reportRouteError(ctx.pathname, error);\n await ctx.navigate({ path: '/error' }, { replace: true });\n }\n};\n\nconst router = createRouter({\n middleware: [boundary],\n routes,\n});\n\n// Check after navigation:\nconst { status, error } = router.getSnapshot();\nif (status === 'error') {\n console.error(error);\n}\n```\n\n## Navigation\n\n### Named Navigation\n\n```ts\nawait router.navigate({ name: 'userDetail', params: { id: '42' } });\nawait router.navigate({ name: 'userDetail', params: { id: '42' } }, { replace: true });\nawait router.navigate({ name: 'search', query: { q: 'wayfinder' }, hash: 'results' });\nawait router.navigate({ name: 'dashboard.settings' });\n```\n\n### Raw Path Targets\n\n```ts\nawait router.navigate({ path: '/marketing?utm_source=campaign' });\nawait router.navigate({ path: '/checkout#payment' }, { replace: true });\n```\n\nUse these when a destination does not belong in the route table. The same `navigate()` method covers named routes and raw path targets.\n\n### History State\n\nAttach arbitrary state to a history entry and read it back via `ctx.historyState` or `router.getSnapshot().location.historyState`.\n\n```ts\nawait router.navigate({ name: 'userDetail', params: { id: '42' } }, { state: { from: 'search' } });\n\n// In data():\ndata: async (ctx) => {\n console.log(ctx.historyState); // { from: 'search' }\n return fetchUser(ctx.params.id);\n},\n```\n\n### Same-URL Deduplication\n\n```ts\nawait router.navigate({ name: 'dashboard' });\nawait router.navigate({ name: 'dashboard' }); // no-op\nawait router.navigate({ name: 'dashboard' }, { force: true }); // re-runs\n```\n\n### Prefetching\n\nEagerly run data loaders without navigating — useful for hover-prefetch:\n\n```ts\n// Preload a parameterised route\nanchor.addEventListener('mouseenter', () => {\n router.preload('userDetail', { id: '42' });\n});\n\n// Preload with a query string to avoid a cache miss on navigation\nsearchInput.addEventListener('focus', () => {\n router.preload('search', undefined, { q: searchInput.value });\n});\n```\n\nConcurrent calls for the same `name + params + query` combination are deduplicated. Results are consumed on the next navigation to the same route with the same cache key. Pass the same `query` you intend to navigate with — without it, the preload key is the bare path and any navigation with a query string will re-run the loaders.\n\nIn-flight preloads are aborted automatically when `router.dispose()` is called.\n\n### Leave Guards\n\nGuard navigation until the user confirms — useful for unsaved-changes forms:\n\n```ts\nconst removeGuard = router.beforeLeave(async (destination) => {\n if (!form.isDirty) return true;\n return confirm('Discard changes?');\n});\n\n// Remove when the component unmounts:\nremoveGuard();\n```\n\nScope a guard to a specific route so it only fires when leaving that route:\n\n```ts\nrouter.beforeLeave(async () => confirm('Discard changes?'), { routes: ['editor'] });\n```\n\n## URLs and Active State\n\n```ts\nrouter.url('userDetail', { id: '42' });\nrouter.url('userDetail', { id: '42' }, { tab: 'profile' });\n\nrouter.isActive('userDetail');\nrouter.isActive('users');\nrouter.isActive('users', { exact: true });\n```\n\n`isActive(name)` reads the current router snapshot and is useful for parent navigation items.\n\n## Match a Path Without Navigating\n\n```ts\nconst branch = router.match('/app/dashboard/settings');\n\nif (branch?.at(-1)?.name === 'dashboard.settings') {\n warmSettingsPanel();\n}\n```\n\n`match()` strips the configured base automatically and returns the full matched branch (root to leaf). Data loaders are not executed.\n\n## Load a Path for SSR\n\nUse `router.load(url)` to load a full route state including data loader results without modifying router state or history. This is useful for server-side data prefetching.\n\n```ts\nconst state = await router.load('/users/42');\n\nif (state) {\n const data = state.matches.at(-1)?.data;\n // serialize and send to the client\n}\n```\n\nPass an `AbortSignal` via the options object to cancel in-flight loaders:\n\n```ts\nconst controller = new AbortController();\nconst state = await router.load('/users/42', { signal: controller.signal });\n```\n\n`load()` follows declarative redirects (up to five hops) and resolves lazy modules as a side effect.\n\n## State and Subscriptions\n\n```ts\nrouter.subscribe((state) => {\n const leaf = state.matches.at(-1);\n document.title = (leaf?.meta as { title?: string } | undefined)?.title ?? 'App';\n});\n```\n\nUse `router.getSnapshot()` to read the current state synchronously:\n\n```ts\nconst { location, matches, status, error } = router.getSnapshot();\n\nlocation.pathname;\nlocation.query; // raw parsed query strings (QueryParams)\nlocation.hash;\nlocation.historyState; // state from the current history entry\n\nmatches; // matched branch from root to leaf\nstatus; // 'idle' | 'loading' | 'streaming' | 'error'\nerror; // only set when status === 'error'\n```\n\nEach match node also carries its own `status`:\n\n```ts\nmatches.at(-1)?.status; // 'idle' | 'loading' | 'streaming' | 'error'\n```\n\nThis lets nested layouts show per-slot loading indicators without polling the top-level status.\n\nThe state object is immutable. A successful navigation replaces it with a new snapshot.\n\n### `waitFor(name)`\n\nWait for the router to reach `status: 'idle'` with a specific route active. Useful in tests and lifecycle coordination:\n\n```ts\n// Navigate and wait for data to settle\nawait router.navigate({ name: 'userDetail', params: { id: '42' } });\nconst state = await router.waitFor('userDetail');\nconst user = state.matches.at(-1)?.data;\n```\n\n`waitFor` rejects immediately if the router is already in `status: 'error'`, and also rejects if `router.dispose()` is called while the promise is pending. Resolves immediately if the named route is already active and idle.\n\n## Scroll Restoration\n\nProvide a `scroll` callback to control scroll position after each navigation:\n\n```ts\nconst router = createRouter({\n routes,\n scroll: (to, from) => {\n // Return 'top' to scroll to top\n // Return { x, y } for a specific position\n // Return 'preserve' to do nothing\n return 'top';\n },\n});\n```\n\nThe callback receives the incoming state and the previous state, making it possible to implement saved-position restore:\n\n```ts\nconst scrollPositions = new Map<string, { x: number; y: number }>();\n\nrouter.subscribe((state) => {\n scrollPositions.set(state.location.pathname, { x: window.scrollX, y: window.scrollY });\n});\n\nconst router = createRouter({\n routes,\n scroll: (to, _from) => scrollPositions.get(to.location.pathname) ?? 'top',\n});\n```\n\n## Testing\n\nUse `createMemoryHistory` to test routers without a browser:\n\n```ts\nimport { createMemoryHistory, createRouter } from '@vielzeug/wayfinder';\n\nconst history = createMemoryHistory('/dashboard');\nconst router = createRouter({ history, routes });\n\n// Use waitFor to avoid manual timing:\nconst state = await router.waitFor('dashboard');\nassert(state.location.pathname === '/dashboard');\n\nrouter.dispose();\n```\n\n## Cleanup\n\n```ts\nrouter.dispose();\n```\n\nRemove listeners, clear subscribers, and prevent future router usage.\n\n## Framework Integration\n\nRoute exposes `getSnapshot()` and `subscribe()`, which map directly to each framework's external-store primitives. Create the router once at module scope and bind actions outside the component lifecycle so references stay stable.\n\n::: code-group\n\n```tsx [React]\nimport { createRouter } from '@vielzeug/wayfinder';\nimport { useSyncExternalStore } from 'react';\n\nconst router = createRouter({\n routes: {\n home: { component: HomePage, path: '/' },\n settings: { component: SettingsPage, path: '/settings' },\n },\n notFound: { component: NotFoundPage },\n});\n\n// Stable router actions are safe to destructure outside the hook.\nconst { getSnapshot, isActive, navigate, subscribe, url } = router;\n\nexport function useRouter() {\n const state = useSyncExternalStore(subscribe, getSnapshot);\n return { isActive, navigate, state, url };\n}\n\n// RouterView.tsx\nexport function RouterView() {\n const { state } = useRouter();\n const Component = state.matches.at(-1)?.component as React.ComponentType | undefined;\n return Component ? <Component /> : null;\n}\n```\n\n```ts [Vue 3]\nimport { createRouter } from '@vielzeug/wayfinder';\nimport { readonly, shallowRef } from 'vue';\n\nconst router = createRouter({\n routes: {\n home: { component: HomePage, path: '/' },\n settings: { component: SettingsPage, path: '/settings' },\n },\n notFound: { component: NotFoundPage },\n});\n\n// shallowRef — no need to deep-track immutable route state.\nconst state = shallowRef(router.getSnapshot());\nrouter.subscribe((next) => {\n state.value = next;\n});\n\nexport function useRouter() {\n const { isActive, navigate, url } = router;\n\n return { isActive, navigate, state: readonly(state), url };\n}\n```\n\n```svelte [Svelte]\n<!-- router.ts -->\n<script lang=\"ts\" context=\"module\">\n import { createRouter } from '@vielzeug/wayfinder';\n import { readable } from 'svelte/store';\n\n const router = createRouter({\n routes: {\n home: { component: HomePage, path: '/' },\n settings: { component: SettingsPage, path: '/settings' },\n },\n notFound: { component: NotFoundPage },\n });\n\n // readable injects the initial value; subscribe() drives updates.\n export const routerState = readable(router.getSnapshot(), (set) => router.subscribe(set));\n export const { isActive, navigate, url } = router;\n</script>\n```\n\n:::\n\nFor full RouterView and RouterLink patterns, see [React Integration](./examples/react-integration.md), [Vue Integration](./examples/vue-integration.md), and [Svelte Integration](./examples/svelte-integration.md).\n\n## Debug Logging\n\n`router.subscribe()` is the reactive subscription API — it receives every state change, including `loading`, `streaming`, and `error` transitions. Attach a listener that logs to `console.debug` to inspect navigation without any dedicated debug tooling.\n\n```ts\nimport { createRouter } from '@vielzeug/wayfinder';\n\nconst router = createRouter({ routes });\nconst stop = router.subscribe((state) => {\n console.debug(`[wayfinder] ${state.status} ${state.location.pathname}`);\n});\n\n// Logged once the initial navigation completes:\n// [wayfinder] idle /\n\n// On navigate({ name: 'dashboard' }):\n// [wayfinder] loading /dashboard\n// [wayfinder] idle /dashboard\n```\n\nThe returned function unsubscribes the listener — call it when the logger is no longer needed (e.g. on teardown):\n\n```ts\nstop();\n```\n\nErrors are surfaced on the state object, so you can log them explicitly:\n\n```ts\nrouter.subscribe((state) => {\n if (state.status === 'error') {\n console.error(`[wayfinder] ${state.location.pathname}`, state.error);\n }\n});\n```\n\nUse a label when running multiple routers to distinguish their log output:\n\n```ts\nconst main = createRouter({ routes });\nmain.subscribe((state) => console.debug(`[wayfinder:main] ${state.status} ${state.location.pathname}`));\n\nconst modal = createRouter({ routes: modalRoutes });\nmodal.subscribe((state) => console.debug(`[wayfinder:modal] ${state.status} ${state.location.pathname}`));\n```\n\nDebug logging has no effect on behavior and should not be enabled in production.\n\n::: tip Unhandled router errors\nIf a route's data loader throws and no `onError` callback is set on the router, the error is surfaced via `console.error` in development and silenced in production (`__WAYFINDER_PROD__` set). Always provide an `onError` callback in production to handle errors explicitly.\n:::\n\n## Working with Other Vielzeug Libraries\n\n### With Ward\n\nUse Ward inside Wayfinder middleware to guard protected routes.\n\n```ts\nimport { createRouter } from '@vielzeug/wayfinder';\nimport { createWard } from '@vielzeug/ward';\n\ntype User = { id: string; roles: string[] };\n\nconst ward = createWard([{ role: 'admin', resource: 'settings', action: 'view', effect: 'allow' }]);\n\nconst router = createRouter({\n middleware: [\n (ctx, next) => {\n const user: User = getSessionUser();\n if (!ward.can(user, 'settings', 'view')) return ctx.navigate({ path: '/login' }, { replace: true });\n return next();\n },\n ],\n routes: {\n settings: { path: '/settings' },\n },\n});\n```\n\n### With Ripple\n\nSync router state to a Ripple signal for reactive UI.\n\n```ts\nimport { createRouter } from '@vielzeug/wayfinder';\nimport { signal } from '@vielzeug/ripple';\n\nconst router = createRouter({\n /* ... */\n});\nconst currentRoute = signal(router.getSnapshot().matches.at(-1)?.name ?? '');\n\nrouter.subscribe((state) => {\n currentRoute.value = state.matches.at(-1)?.name ?? '';\n});\n```\n\n## Best Practices\n\n- Define the route table once at app startup and import it where needed.\n- Prefer named navigation (`router.navigate({ name: 'settings' })`) over raw paths.\n- Put auth and permission checks in middleware, not in data loaders.\n- Use `data()` loaders for route data and honor the provided `AbortSignal`.\n- Use `onError` on a route for degraded-state rendering rather than a full redirect to an error page.\n- Use `notFound` in router options for the not-found page rather than `path: '*'` in the route table.\n- Call `router.dispose()` when tearing down apps/tests to release listeners.\n- Use `createMemoryHistory()` for tests and non-browser runtimes; avoid touching `window.history` directly.\n- Use `router.preload()` on hover for routes likely to be visited next.\n",
7
- "examples": "---\ntitle: Wayfinder — Examples\ndescription: Practical examples and recipes for wayfinder.\n---\n\n## Examples\n\n- [Route Table Basics](./examples/route-table-basics.md)\n- [Not Found and Error Boundary](./examples/not-found-and-error-boundary.md)\n- [Auth and Guards](./examples/auth-and-guards.md)\n- [Page Titles from Meta](./examples/page-titles-from-meta.md)\n- [Same-URL Deduplication](./examples/same-url-deduplication.md)\n- [Base Path Deployment](./examples/base-path-deployment.md)\n- [Raw Path Targets](./examples/raw-path-targets.md)\n- [View Transitions](./examples/view-transitions.md)\n- [React Integration](./examples/react-integration.md)\n- [Vue Integration](./examples/vue-integration.md)\n- [Svelte Integration](./examples/svelte-integration.md)\n"
8
- },
9
- "examples": [
10
- {
11
- "id": "basic-routing",
12
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// Named routes, typed params, and subscribe() for reactive rendering.\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n about: { path: '/about', data: async () => ({ title: 'About Us' }) },\n userDetail: { path: '/users/:id', data: async ({ params }) => ({ id: params.id, name: 'User ' + params.id }) },\n },\n notFound: {},\n})\n\n// React to every state change — the router notifies on navigate and load.\nrouter.subscribe((state) => {\n const leaf = state.matches.at(-1)\n if (state.status === 'idle') {\n console.log('route:', leaf?.name, '| data:', JSON.stringify(leaf?.data))\n }\n})\n\nawait router.ready\nconsole.log('Initial pathname:', router.getSnapshot().location.pathname)\n\nawait router.navigate({ name: 'about' })\nawait router.navigate({ name: 'userDetail', params: { id: '42' } })\n\nconsole.log('Current pathname:', router.getSnapshot().location.pathname)\nconsole.log('Params:', router.getSnapshot().matches.at(-1)?.params)\n\nrouter.dispose()",
13
- "name": "Basic Routing — Route State and Navigation"
14
- },
15
- {
16
- "id": "debug-router",
17
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n userDetail: { path: '/users/:id', data: async ({ params }) => ({ id: params.id }) },\n settings: { path: '/settings' },\n },\n})\n\n// Observe navigation state changes via subscribe()\nrouter.subscribe((state) => {\n const names = state.matches.map((m) => m.name).filter(Boolean).join(', ')\n console.debug(`[wayfinder] ${state.status} ${state.location.pathname} [${names}]`)\n})\n\nawait router.ready\nawait router.navigate({ name: 'userDetail', params: { id: '42' } })\nawait router.navigate({ name: 'settings' })\n\nconsole.log('active route:', router.getSnapshot().matches.at(-1)?.name)\nrouter.dispose()",
18
- "name": "Navigation Logging"
19
- },
20
- {
21
- "id": "middleware-auth",
22
- "code": "import { createMemoryHistory, createRouter, redirectTo } from '@vielzeug/wayfinder'\n\n// Middleware runs before data(); use it for auth checks, redirects, and analytics.\nconst session = { currentUser: null }\n\nconst requireAuth = async (ctx, next) => {\n if (!session.currentUser) {\n console.log('not authenticated — redirecting to /login')\n await ctx.navigate({ name: 'login' }, { replace: true })\n return // do not call next(); cancels navigation to the protected route\n }\n ctx.locals.user = session.currentUser\n await next()\n}\n\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n login: { path: '/login' },\n dashboard: {\n path: '/dashboard',\n middleware: [requireAuth],\n data: (ctx) => ({ welcome: 'Hello, ' + ctx.locals.user.name }),\n },\n // redirectTo() is shorthand for an unconditional redirect middleware.\n legacy: { path: '/old-dashboard', middleware: [redirectTo({ name: 'dashboard' }, { replace: true })] },\n },\n})\n\nconsole.log('--- unauthenticated ---')\nawait router.navigate({ name: 'dashboard' })\nconsole.log('location after blocked nav:', router.getSnapshot().location.pathname)\n\nsession.currentUser = { name: 'Alice' }\nconsole.log('--- authenticated ---')\nawait router.navigate({ name: 'dashboard' })\nconsole.log('data:', JSON.stringify(router.getSnapshot().matches.at(-1)?.data))\n\nconsole.log('--- legacy redirect ---')\nawait router.navigate({ path: '/old-dashboard' })\nconsole.log('location after redirect:', router.getSnapshot().location.pathname)\n\nrouter.dispose()",
23
- "name": "Guards and Redirects — Auth Flows"
24
- },
25
- {
26
- "id": "middleware-chain",
27
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// Execution order is always: global middleware → route middleware → data().\nconst logger = async (ctx, next) => {\n console.log('[global] entering', ctx.pathname)\n await next()\n console.log('[global] leaving', ctx.pathname)\n}\n\nconst loadUser = async (ctx, next) => {\n console.log('[route] loading user')\n ctx.locals.user = { id: 1, name: 'Alice', role: 'admin' }\n await next()\n}\n\nconst requireAdmin = async (ctx, next) => {\n console.log('[route] checking role:', ctx.locals.user?.role)\n if (ctx.locals.user?.role !== 'admin') {\n console.log('[route] permission denied — aborting navigation')\n return // do not call next(); cancels the navigation\n }\n await next()\n}\n\nconst router = createRouter({\n history: createMemoryHistory('/'),\n middleware: [logger],\n routes: {\n home: { path: '/' },\n admin: {\n path: '/admin',\n middleware: [loadUser, requireAdmin],\n data: async (ctx) => {\n console.log('[data()] fetching panel data for', ctx.locals.user.name)\n return { loaded: true, user: ctx.locals.user.name }\n },\n },\n },\n})\n\nawait router.waitFor('home')\nconsole.log('--- navigate to /admin ---')\nawait router.navigate({ name: 'admin' })\nconsole.log('data:', JSON.stringify(router.getSnapshot().matches.at(-1)?.data))\n\nrouter.dispose()",
28
- "name": "Middleware Chain — Execution Flow"
29
- },
30
- {
31
- "id": "named-routes",
32
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// Route keys become type-safe names; url() and navigate() reference them by name.\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n users: { path: '/users' },\n userDetail: { path: '/users/:id' },\n postComment: { path: '/posts/:postId/comments/:commentId' },\n },\n})\n\n// Build base-aware URLs without navigating.\nconsole.log('userDetail url:', router.url('userDetail', { id: '123' }))\nconsole.log('postComment url:', router.url('postComment', { postId: '10', commentId: '50' }))\nconsole.log('url with query:', router.url('users', undefined, { page: 2 }))\n\n// Navigate by name — TypeScript will enforce the required params shape.\nawait router.navigate({ name: 'userDetail', params: { id: '42' } })\n\n// isActive() defaults to prefix matching — useful for parent nav items.\nconsole.log('userDetail isActive (prefix):', router.isActive('userDetail'))\nconsole.log('users isActive (prefix):', router.isActive('users'))\nconsole.log('users isActive (exact):', router.isActive('users', { exact: true }))\n\n// match() returns the matched branch without navigating or running data().\nconst branch = router.match('/users/99')\nconsole.log('matched name:', branch?.at(-1)?.name)\nconsole.log('matched params:', branch?.at(-1)?.params)\n\nrouter.dispose()",
33
- "name": "Named Routes — Type-Safe Navigation"
34
- },
35
- {
36
- "id": "nested-routes",
37
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// Child route names use dot notation: 'dashboard.settings', 'dashboard.index'.\nconst router = createRouter({\n history: createMemoryHistory('/dashboard'),\n routes: {\n dashboard: {\n path: '/dashboard',\n data: async () => ({ section: 'Dashboard' }), // parent data runs for every child\n children: {\n index: { index: true }, // inherits parent path\n settings: { path: 'settings', data: async () => ({ view: 'settings' }) },\n audit: { path: 'audit', data: async () => ({ view: 'audit' }) },\n },\n },\n blogPost: {\n path: '/blog/posts/:id',\n data: async ({ params }) => ({ postId: params.id }),\n },\n },\n})\n\n// The initial match is dashboard.index (index: true).\nconst initial = await router.waitFor('dashboard.index')\nconsole.log('initial branch:', initial.matches.map((m) => m.name))\n\nawait router.navigate({ name: 'dashboard.settings' })\nconst snap = router.getSnapshot()\nconsole.log('settings branch:', snap.matches.map((m) => m.name))\nconsole.log('leaf data:', JSON.stringify(snap.matches.at(-1)?.data))\nconsole.log('audit url:', router.url('dashboard.audit'))\n\nawait router.navigate({ name: 'blogPost', params: { id: '123' } })\nconsole.log('blog data:', JSON.stringify(router.getSnapshot().matches.at(-1)?.data))\n\nrouter.dispose()",
38
- "name": "Nested Routes — Children and Index Routes"
39
- },
40
- {
41
- "id": "preload-and-dispose",
42
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\nlet fetchCount = 0\n\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n product: {\n path: '/products/:id',\n data: async ({ params }) => {\n fetchCount++\n return { id: params.id, name: 'Product ' + params.id, fetchCount }\n },\n },\n search: {\n path: '/search',\n data: async ({ query }) => {\n fetchCount++\n return { results: ['a', 'b', 'c'], q: query.q, fetchCount }\n },\n },\n },\n})\n\n// ── Preload with params (no query) ───────────────────────────────────────────\n// Warm the data loader before navigation — simulates hover prefetch.\nawait router.preload('product', { id: '99' })\nconsole.log('fetches after product preload:', fetchCount) // 1\n\n// Navigate — data loader is NOT called again (cache hit).\nawait router.navigate({ name: 'product', params: { id: '99' } })\nconsole.log('fetches after product navigate:', fetchCount) // still 1\n\n// ── Preload with query param ──────────────────────────────────────────────────\n// Pass the same query you intend to navigate with so the cache key matches.\nawait router.navigate({ path: '/' })\nawait router.preload('search', undefined, { q: 'hello' })\nconsole.log('fetches after search preload:', fetchCount) // 2\n\n// Navigate with the same query — cache hit, no extra fetch.\nawait router.navigate({ name: 'search', query: { q: 'hello' } })\nconsole.log('fetches after search navigate:', fetchCount) // still 2\nconsole.log('search data:', router.getSnapshot().matches.at(-1)?.data)\n\n// ── Lifecycle ─────────────────────────────────────────────────────────────────\nconsole.log('disposed before dispose():', router.disposed)\nrouter.dispose()\nconsole.log('disposed after dispose():', router.disposed)\nconsole.log('disposalSignal aborted:', router.disposalSignal.aborted)",
43
- "name": "Preload Cache and Dispose"
44
- },
45
- {
46
- "id": "query-params",
47
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// coerceSearch normalises raw URL strings into typed values before data() runs.\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n search: {\n path: '/search',\n coerceSearch: (raw) => ({\n page: Math.max(1, Number(raw.page ?? 1)),\n q: String(raw.q ?? ''),\n tags: Array.isArray(raw.tags) ? raw.tags : raw.tags ? [raw.tags] : [],\n }),\n data: async ({ query }) => ({\n // ctx.query here is the coerced result, not raw URL strings.\n results: `searched \"${query.q}\" page ${query.page} tags:${query.tags}`,\n }),\n },\n userPosts: {\n path: '/users/:id/posts',\n data: async ({ params, query }) => ({\n userId: params.id,\n status: query.status ?? 'all',\n limit: Number(query.limit ?? 10),\n }),\n },\n },\n})\n\nawait router.navigate({ name: 'search', query: { page: 2, q: 'wayfinder', tags: ['docs', 'routing'] } })\nconsole.log('search data:', JSON.stringify(router.getSnapshot().matches.at(-1)?.data))\n\nawait router.navigate({ name: 'userPosts', params: { id: '42' }, query: { status: 'published', limit: 20 } })\nconsole.log('posts data:', JSON.stringify(router.getSnapshot().matches.at(-1)?.data))\n\n// Raw URL query is always string values; coerced values live in ctx.query.\nconst loc = router.getSnapshot().location\nconsole.log('raw location.query:', JSON.stringify(loc.query))\n\nrouter.dispose()",
48
- "name": "Query Parameters — Coercion and URL State"
49
- },
50
- {
51
- "id": "route-context",
52
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// RouteContext is the full object available in middleware and data().\n// Middleware receives ctx without a 'data' property; data() adds the signal.\nconst router = createRouter({\n history: createMemoryHistory('/'),\n routes: {\n home: { path: '/' },\n postDetail: {\n path: '/users/:userId/posts/:postId',\n meta: { title: 'Post Detail', breadcrumbs: ['Home', 'Users', 'Posts'] },\n middleware: [\n async (ctx, next) => {\n // Middleware can read params, query, hash, historyState, locals, navigate.\n ctx.locals.user = { id: Number(ctx.params.userId), name: 'Alice' }\n console.log('middleware | pathname:', ctx.pathname)\n console.log('middleware | params: ', JSON.stringify(ctx.params))\n console.log('middleware | query: ', JSON.stringify(ctx.query))\n console.log('middleware | state: ', JSON.stringify(ctx.historyState))\n await next()\n },\n ],\n data: async (ctx) => {\n // data() gets the same context plus an AbortSignal for cancellation.\n console.log('data() | user from locals:', ctx.locals.user.name)\n console.log('data() | leaf meta:', JSON.stringify(ctx.matches.at(-1)?.meta))\n return { postId: ctx.params.postId, author: ctx.locals.user.name }\n },\n },\n },\n})\n\nawait router.navigate(\n { name: 'postDetail', params: { userId: '42', postId: '123' }, query: { tab: 'comments' } },\n { state: { from: 'feed' } },\n)\n\nconsole.log('snapshot data:', JSON.stringify(router.getSnapshot().matches.at(-1)?.data))\nrouter.dispose()",
53
- "name": "Route Context — Full Context Access"
54
- },
55
- {
56
- "id": "url-building",
57
- "code": "import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder'\n\n// url(), match(), and isActive() are synchronous and do not modify router state.\nconst router = createRouter({\n base: '/app',\n history: createMemoryHistory('/app/users/123'),\n routes: {\n users: { path: '/users' },\n user: { path: '/users/:id' },\n comment: { path: '/posts/:postId/comments/:commentId' },\n search: { path: '/search' },\n },\n})\n\n// Wait for the initial navigation to settle before reading active state.\nawait router.waitFor('user')\n\nconsole.log('--- url() ---')\nconsole.log('user: ', router.url('user', { id: '42' }))\nconsole.log('search: ', router.url('search', undefined, { q: 'typescript', page: 2 }))\nconsole.log('comment:', router.url('comment', { postId: '10', commentId: '25' }))\n\nconsole.log('--- match() ---')\nconst branch = router.match('/app/users/99')\nconsole.log('matched:', branch?.map((n) => n.name + ' params=' + JSON.stringify(n.params)))\nconsole.log('no match:', router.match('/app/does-not-exist'))\n\nconsole.log('--- isActive() ---')\nconsole.log('user (prefix):', router.isActive('user'))\nconsole.log('users (prefix):', router.isActive('users')) // true — /users prefix matches /users/123\nconsole.log('users (exact):', router.isActive('users', { exact: true })) // false\n\nrouter.dispose()",
58
- "name": "URL Building — Path Matching and Active State"
59
- }
60
- ],
61
- "typeSignatures": {
62
- "WayfinderApiError": "export {\n WayfinderApiError,\n WayfinderDisposedError,\n WayfinderError,\n WayfinderRedirectLoopError,\n WayfinderRouteError,\n} from './errors';",
63
- "WayfinderDisposedError": "export {\n WayfinderApiError,\n WayfinderDisposedError,\n WayfinderError,\n WayfinderRedirectLoopError,\n WayfinderRouteError,\n} from './errors';",
64
- "WayfinderError": "export {\n WayfinderApiError,\n WayfinderDisposedError,\n WayfinderError,\n WayfinderRedirectLoopError,\n WayfinderRouteError,\n} from './errors';",
65
- "WayfinderRedirectLoopError": "export {\n WayfinderApiError,\n WayfinderDisposedError,\n WayfinderError,\n WayfinderRedirectLoopError,\n WayfinderRouteError,\n} from './errors';",
66
- "WayfinderRouteError": "export {\n WayfinderApiError,\n WayfinderDisposedError,\n WayfinderError,\n WayfinderRedirectLoopError,\n WayfinderRouteError,\n} from './errors';",
67
- "createBrowserHistory": "export { createBrowserHistory, createMemoryHistory } from './history';",
68
- "createMemoryHistory": "export { createBrowserHistory, createMemoryHistory } from './history';",
69
- "redirectTo": "export { redirectTo } from './middleware';",
70
- "Router": "export type { Router } from './router';",
71
- "createRouter": "export { createRouter } from './router';",
72
- "BeforeLeaveBlocker": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
73
- "BeforeLeaveOptions": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
74
- "CoerceSearchFn": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
75
- "DataContext": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
76
- "DataFn": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
77
- "DataStream": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
78
- "HistoryDriver": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
79
- "IsActiveOptions": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
80
- "MaybePromise": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
81
- "Middleware": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
82
- "NamedNavigationTarget": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
83
- "NavigateOptions": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
84
- "NavigationDestination": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
85
- "NavigationStatus": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
86
- "NavigationTarget": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
87
- "PathParams": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
88
- "QueryParams": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
89
- "RawNavigationTarget": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
90
- "ResolvedQueryParams": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
91
- "ResolvedQueryValue": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
92
- "RouteContext": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
93
- "RouteDefinition": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
94
- "RouteLocation": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
95
- "RouteMatch": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
96
- "RouteMatchBranch": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
97
- "RouteMiddleware": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
98
- "RouteName": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
99
- "RouteParams": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
100
- "RoutePathByName": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
101
- "RouterErrorContext": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
102
- "RouterOptions": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
103
- "RouteState": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
104
- "RouteTable": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
105
- "ScrollDecision": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
106
- "ScrollPosition": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
107
- "Unsubscribe": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';",
108
- "UntypedNamedNavigationTarget": "export type {\n BeforeLeaveBlocker,\n BeforeLeaveOptions,\n CoerceSearchFn,\n DataContext,\n DataFn,\n DataStream,\n HistoryDriver,\n IsActiveOptions,\n MaybePromise,\n Middleware,\n NamedNavigationTarget,\n NavigateOptions,\n NavigationDestination,\n NavigationStatus,\n NavigationTarget,\n PathParams,\n QueryParams,\n RawNavigationTarget,\n ResolvedQueryParams,\n ResolvedQueryValue,\n RouteContext,\n RouteDefinition,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteMiddleware,\n RouteName,\n RouteParams,\n RoutePathByName,\n RouterErrorContext,\n RouterOptions,\n RouteState,\n RouteTable,\n ScrollDecision,\n ScrollPosition,\n Unsubscribe,\n UntypedNamedNavigationTarget,\n} from './types';"
109
- }
110
- }