import { ComponentChildren, Context, FunctionComponent, HTMLAttributes, VNode } from "preact"; import { NavigationOptions, NavigationTarget, Navigator, Navigator as Navigator$1, Params, Router, RouterError, SearchParams, State } from "@real-router/core"; import { RouteUtils } from "@real-router/route-utils"; import { RouterTransitionSnapshot, RouterTransitionSnapshot as RouterTransitionSnapshot$1 } from "@real-router/sources"; //#region src/components/RouteView/types.d.ts interface RouteViewProps { readonly nodeName: string; readonly children: ComponentChildren; } interface MatchProps { readonly segment: string; readonly exact?: boolean; readonly fallback?: ComponentChildren; readonly children: ComponentChildren; } interface SelfProps { /** Fallback content while children are suspended. */ readonly fallback?: ComponentChildren; /** Content to render when the active route name equals the parent RouteView's nodeName. */ readonly children: ComponentChildren; } interface NotFoundProps { readonly children: ComponentChildren; } //#endregion //#region src/components/RouteView/components.d.ts declare function Match(_props: MatchProps): null; declare namespace Match { var displayName: string; } declare function Self(_props: SelfProps): null; declare namespace Self { var displayName: string; } declare function NotFound(_props: NotFoundProps): null; declare namespace NotFound { var displayName: string; } //#endregion //#region src/components/RouteView/RouteView.d.ts declare function RouteViewRoot({ nodeName, children }: Readonly): VNode | null; declare namespace RouteViewRoot { var displayName: string; } export declare const RouteView: typeof RouteViewRoot & { Match: typeof Match; Self: typeof Self; NotFound: typeof NotFound; }; //#endregion //#region src/types.d.ts interface RouteState

{ route: State

| undefined; previousRoute?: State | undefined; } type RouteContext$1

= RouteState

& { navigator: Navigator$1; }; /** Props shared by both `` forms (see react `LinkProps` for the rationale). */ interface LinkCommonProps extends Omit, "className"> { routeOptions?: NavigationOptions; className?: string; activeClassName?: string; activeStrict?: boolean; ignoreQueryParams?: boolean; /** * URL fragment (decoded form, no leading "#") (#532). * - omitted/`undefined` → preserve current fragment on same-route navigation * - `""` → clear fragment * - non-empty → set fragment * * Requires a URL plugin (browser-plugin or navigation-plugin) for full * round-trip; hash-plugin ignores the prop with a one-time dev warning. */ hash?: string; target?: string; } /** Channel form: `routeName` + optional `routeParams` / `routeSearch`. */ interface LinkChannelProps

{ routeName: string; routeParams?: P; /** * Query (search) params for the link's target (RFC-4 M2, #1548) — parallel to * `routeParams`, the path/query split's view-layer channel. */ routeSearch?: SearchParams; to?: never; } /** Descriptor form (RFC-4 M2 B2, #1548): `to={NavigationTarget}`, exclusive with channel props. */ interface LinkDescriptorProps

{ to: NavigationTarget

; routeName?: never; routeParams?: never; routeSearch?: never; } type LinkProps

= LinkCommonProps & (LinkChannelProps

| LinkDescriptorProps

); //#endregion //#region src/components/Link.d.ts export declare const Link: FunctionComponent; //#endregion //#region src/components/RouterErrorBoundary.d.ts interface RouterErrorBoundaryProps { readonly children: ComponentChildren; readonly fallback: (error: RouterError, resetError: () => void) => ComponentChildren; readonly onError?: (error: RouterError, toRoute: State | null, fromRoute: State | null) => void; } /** * Declarative navigation-error boundary. * * **Not** a Preact `componentDidCatch`-style ErrorBoundary — this component * does NOT catch render-time exceptions from `children`. It is a compositional * component that subscribes to `createDismissableError` from * `@real-router/sources` and renders `fallback(error, resetError)` ALONGSIDE * `children` (wrapped in a ``) when the router emits a navigation * error (guard rejection, ROUTE_NOT_FOUND, etc.). The boundary auto-resets on * the next successful navigation; `resetError()` lets the consumer dismiss * the fallback imperatively. * * For real exception boundaries, wrap children in a Preact ErrorBoundary * (e.g. `preact-iso/ErrorBoundary` or a custom `componentDidCatch` class) — * the two can coexist. */ export declare function RouterErrorBoundary({ children, fallback, onError }: RouterErrorBoundaryProps): VNode; //#endregion //#region src/hooks/useRouter.d.ts export declare const useRouter: () => Router; //#endregion //#region src/hooks/useNavigator.d.ts export declare const useNavigator: () => Navigator$1; //#endregion //#region src/hooks/useRouteUtils.d.ts export declare const useRouteUtils: () => RouteUtils; //#endregion //#region src/hooks/useRoute.d.ts export declare const useRoute:

() => Omit, "route"> & { route: State

; }; //#endregion //#region src/hooks/useRouteNode.d.ts export declare function useRouteNode(nodeName: string): RouteContext$1; //#endregion //#region src/hooks/useRouterTransition.d.ts export declare function useRouterTransition(): RouterTransitionSnapshot$1; //#endregion //#region src/hooks/useRouteExit.d.ts interface RouteExitContext { /** The route being left. */ route: State; /** The route being navigated to. */ nextRoute: State; /** * AbortSignal that fires when this navigation is superseded by a later * one (rapid clicks). Already filtered: when the handler runs, * `signal.aborted` is guaranteed to be `false`. Use * `signal.addEventListener("abort", cleanup, { once: true })` for * cleanup that must run on cancellation. */ signal: AbortSignal; } interface UseRouteExitOptions { /** * Skip the handler when `route.name === nextRoute.name` * (sort/filter/query-only navigations on the same route). Default: * `true`. */ skipSameRoute?: boolean; } type RouteExitHandler = (context: RouteExitContext) => void | Promise; /** * Subscribe to the router's leave-window with the universal guards baked * in. Wraps `router.subscribeLeave` so consumers don't repeat the same * boilerplate every time: * * - **Reentrant abort pre-check**: if `signal.aborted` is already `true` * when the handler would run (rapid navigation superseded a slower * one), the handler is skipped entirely. `signal.addEventListener( * "abort", ...)` does not fire retroactively, so without this guard * downstream cleanup would never trigger. * - **Same-route skip**: by default, `route.name === nextRoute.name` * short-circuits the handler — query-only navigations (sort, filter, * pagination) skip the work. Opt out with `skipSameRoute: false`. * - **Stable handler reference**: the handler can change identity on * every render without causing resubscription — internal ref keeps * the latest handler accessible to the long-lived subscription. * * Returns nothing — the subscription's lifecycle is bound to the * component's mount. * * If the handler returns a Promise, the router blocks on it. If the * Promise resolves, navigation proceeds. If it **rejects**, the router * rejects `navigate()` with the handler's **original error** and emits * `TRANSITION_ERROR` — it is NOT re-coded to `TRANSITION_CANCELLED` * (that arises only when the navigation's `signal` aborts: a superseding * navigation, `stop()`, `dispose()`, or an external `opts.signal`). * * @example Animation * ```tsx * const ref = useRef(null); * * useRouteExit(async ({ signal }) => { * const el = ref.current; * if (!el) return; * el.classList.add("fade-out"); * const cleanup = () => el.classList.remove("fade-out"); * signal.addEventListener("abort", cleanup, { once: true }); * try { * el.getBoundingClientRect(); // style flush * await Promise.allSettled(el.getAnimations().map((a) => a.finished)); * } finally { * cleanup(); * } * }); * ``` * * @example Auto-save form draft * ```tsx * useRouteExit(async ({ signal }) => { * if (formState.dirty) await api.saveDraft(formState, { signal }); * }); * ``` * * @example Cancel inflight requests * ```tsx * useRouteExit(() => { * inflightController.abort(); * }); * ``` * * @example Library-coordinated exit (motion / framer-motion) * ```tsx * const exitResolverRef = useRef<(() => void) | null>(null); * * useRouteExit(({ signal }) => { * return new Promise((resolve) => { * exitResolverRef.current = resolve; * signal.addEventListener("abort", () => resolve(), { once: true }); * }); * }); * * const onExitComplete = () => exitResolverRef.current?.(); * // pass onExitComplete to * ``` * * @example Detecting that you are leaving a subtree * ```tsx * const inProducts = (name: string) => * name === "products" || name.startsWith("products."); * * useRouteExit(({ route, nextRoute }) => { * if (inProducts(route.name) && !inProducts(nextRoute.name)) { * // leaving the products subtree entirely — flush product-related caches * productCache.clear(); * } * }); * ``` * * ⚠ Do NOT read `nextRoute.transition` here. `nextRoute` is the PENDING target, * and the pipeline gives it the neutral default — empty `segments`, optional * flags `undefined` — so the example this replaced read `[]` and `undefined` * whatever the navigation was. (It threw outright until real-router#1976 * attached the field.) The real metadata is written at the COMMIT: read it in * `router.subscribe`, `onTransitionSuccess`, or `getState()`. */ export declare function useRouteExit(handler: RouteExitHandler, options?: UseRouteExitOptions): void; //#endregion //#region src/hooks/useRouteEnter.d.ts interface RouteEnterContext { /** The route that was just activated. */ route: State; /** The route that was active immediately before this navigation. */ previousRoute: State; } type RouteEnterHandler = (context: RouteEnterContext) => void; interface UseRouteEnterOptions { /** * Skip the handler when `route.name === previousRoute.name` * (sort/filter/query-only navigations on the same route). Default: * `true`. Symmetric with `useRouteExit`'s same-name option. */ skipSameRoute?: boolean; } /** * Fire `handler` once when the component mounts as a result of a * navigation. Mirror of `useRouteExit` for the entry side. * * What this hook covers that ad-hoc `useEffect` + `useRoute()` doesn't: * * - **Skip-initial**: handler is skipped when there is no * `previousRoute` (i.e. first-load mount). Most consumers want to * fire side effects only on real navigations, not on hydration. * - **Same-route skip** (default): handler is skipped when * `route.name === previousRoute.name`. Sort/filter/query-only * navigations re-run the effect (because `route` reference changes * in `useRoute`'s snapshot), but they are not "entries" in the * animation / analytics sense — the component instance has stayed * mounted throughout. Opt out with `skipSameRoute: false` when * the handler legitimately needs to fire on every navigation * (e.g. analytics tracking each query-param flip). * - **Latest-handler ref**: the handler can change identity on every * render without re-running the effect — the registered wrapper * dispatches to whatever `handlerRef.current` points to. * - **Mount-time `route` / `previousRoute` snapshot**: the handler * receives the values that were live at the moment of mount, not * the latest ones (which may have moved on if the user navigated * again before the effect drained). * * Race-safety: `useRoute()` is wired through `useSyncExternalStore` from * `@real-router/sources` (Preact polyfill: useState + useEffect, same * post-commit semantics), so by the time the new component's effect * runs, the snapshot is the post-commit one. This is the reason we can * read mount-time context from `useRoute()` instead of subscribing to * `router.subscribe` directly (which fires before Preact schedules a * re-render — the well-known race in distributed components). * * Note: Preact does not expose a `StrictMode` equivalent, so the shared * gate's dedupe arm (`createRouteEnterGate`, `@real-router/sources`) is a * defensive no-op here — kept for parity with React and tested once in * sources. * * @example Direction-aware entry animation * ```tsx * useRouteEnter(({ route }) => { * const direction = route.context.browser?.direction; * ref.current?.classList.add( * direction === "back" ? "slide-from-left" : "slide-from-right", * ); * }); * ``` * * @example Source-aware focus management * ```tsx * useRouteEnter(({ route }) => { * if (route.context.browser?.source === "navigate") { * headingRef.current?.focus(); * } * }); * ``` * * @example Analytics page-enter event (skip-initial built-in) * ```tsx * useRouteEnter(({ route, previousRoute }) => { * analytics.track("page_enter", { * route: route.name, * from: previousRoute.name, * }); * }); * ``` * * @example Reading rich transition metadata via `route.transition` * ```tsx * useRouteEnter(({ route }) => { * // route.transition: TransitionMeta — populated by core for every state * // ⚠ NOT `transition.redirected` — the router never sets it (it only ever * // carries what a caller passed as `{ redirected: true }`), so branching on * // it here is silently dead for a `forwardTo` or a guard redirect. * // `from` needs no check: this hook does not fire without one. * showToast(`Arrived from ${route.transition.from}`); * if (route.transition.segments.activated.includes("products")) { * // products subtree just became active (could be products or * // products.detail). Useful for subtree-scoped side effects. * } * }); * ``` */ export declare function useRouteEnter(handler: RouteEnterHandler, options?: UseRouteEnterOptions): void; //#endregion //#region ../../shared/dom-utils/route-announcer.d.ts interface RouteAnnouncerOptions { prefix?: string; getAnnouncementText?: (route: State) => string; } //#endregion //#region ../../shared/dom-utils/scroll-restore.d.ts type ScrollRestorationMode = "restore" | "top" | "native"; interface ScrollRestorationOptions { mode?: ScrollRestorationMode | undefined; anchorScrolling?: boolean | undefined; scrollContainer?: (() => HTMLElement | null) | undefined; /** * Scroll behavior passed to `scrollTo({ behavior })` and * `scrollIntoView({ behavior })`. * * - `"auto"` (default) — browser-defined, usually instant. * - `"instant"` — explicit instant jump (no animation). * - `"smooth"` — animated transition. Note: smooth restore on back/traverse * can feel disorienting if the user expects to land at the saved position * immediately. Recommended for `mode: "top"` or anchor scroll only. * * See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior). */ behavior?: ScrollBehavior | undefined; /** * sessionStorage key for persisting saved scroll positions. Default: * `"real-router:scroll"`. Override only when multiple independent * `RouterProvider` instances share the same document and you need to * isolate their scroll stores (e.g. micro-frontends, embedded widgets, * or testing). For a single app with one provider the default is fine. */ storageKey?: string | undefined; } //#endregion //#region ../../shared/dom-utils/scroll-spy.d.ts /** * Router-coordinated scroll spy (#575). * * On `IntersectionObserver` notifications the utility picks the topmost * visible anchor inside the configured scroll container and emits a forced * same-route transition with `{ hash, replace: true, force: true, hashChange: * true }` through `router.navigate(...)`. The URL plugin * (`@real-router/browser-plugin` or `@real-router/navigation-plugin`) updates * `state.context.url.hash` so sibling hash-aware `` re-highlights * via the standard `createActiveRouteSource` pipeline. * * **Anti-flicker gates** (RFC §5.2): * 1. `getTransitionSource(router).getSnapshot().isTransitioning` — skip emits * while a transition is in-flight (re-entrant lock). * 2. `coolingDown` — set on a user-driven hash transition (e.g. `` * click + smooth `scrollIntoView`). Cleared on `scrollend` or after a * 500ms safety timeout. Spy's own emits are excluded via the synchronous * `selfEmitting` flag — required so the spy doesn't rate-limit itself. * * **Self-healing** (RFC §7.3): if the initial URL contains a hash without a * matching `id` (e.g. `/page#nonexistent`), the first IO event emitted right * after observe()-ing picks the topmost real anchor and corrects the URL. * * **Hash-only transition pipeline cost** (RFC §5.3): for same-route same- * params hash-only navigations, `getTransitionPath` returns empty * `toDeactivate` / `toActivate` arrays, so `runGuards` is a no-op. The only * work is the URL plugin's `onTransitionSuccess` write and the * `getTransitionSource` flip — cheap. * * **Architecture**: decomposed into 4 private subsystem closure factories * (`createUrlPluginDetector`, `createCooldown`, `createDebouncer`, * `createObserverPair`). The main `createScrollSpy` wires them together * around the shared `silenced` / `destroyed` / `selfEmitting` flags and the * `flush()` emit logic. Each subsystem owns its state + cleanup; `destroy()` * delegates to each. See section banners below. * * @returns A `ScrollSpy` handle whose `destroy()` is idempotent. */ interface ScrollSpyOptions { /** * CSS selector for anchor candidates. Empty string `""` or `undefined` * disables the spy (returns a NOOP handle). Common values: * `"[id]"`, `"[id]:is(h1,h2,h3)"`, `"section[id]"`. */ selector: string; /** * `IntersectionObserver` `rootMargin`. Default * `"-20% 0px -60% 0px"` — an anchor is considered "active" once it crosses * into the top 20 % of the viewport (or scroll container). */ rootMargin?: string | undefined; /** * Lazy getter for the scrollable container. Consulted at creation and * re-consulted on every reconcile (DOM mutation), so a container that * MOUNTS or CHANGES after the spy is created is honoured: the * `IntersectionObserver` root and `MutationObserver` target — both immutable * once constructed — are rebuilt to match (#780). `null` (or a missing * getter) falls back to the window viewport (`root: null` on the * `IntersectionObserver`). */ scrollContainer?: (() => HTMLElement | null) | undefined; } //#endregion //#region src/RouterProvider.d.ts interface RouteProviderProps { router: Router; children: ComponentChildren; announceNavigation?: boolean | RouteAnnouncerOptions; scrollRestoration?: ScrollRestorationOptions; scrollSpy?: ScrollSpyOptions; viewTransitions?: boolean; } export declare const RouterProvider: FunctionComponent; //#endregion //#region src/context.d.ts export declare const RouteContext: Context; export declare const RouterContext: Context | null>; export declare const NavigatorContext: Context; //#endregion export type { LinkProps, Navigator, RouteEnterContext, RouteEnterHandler, RouteExitContext, RouteExitHandler, MatchProps as RouteViewMatchProps, NotFoundProps as RouteViewNotFoundProps, RouteViewProps, SelfProps as RouteViewSelfProps, RouterErrorBoundaryProps, RouterTransitionSnapshot, UseRouteEnterOptions, UseRouteExitOptions }; //# sourceMappingURL=index.d.ts.map