@real-router/react 0.22.0 → 0.23.0

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 (43) hide show
  1. package/README.md +12 -0
  2. package/dist/cjs/Link-C2vVQyg9.js +2 -0
  3. package/dist/cjs/Link-C2vVQyg9.js.map +1 -0
  4. package/dist/cjs/{RouterProvider-DIRzLz4N.d.ts → RouterProvider-CSeAAYD-.d.ts} +3 -3
  5. package/dist/cjs/{RouterProvider-DIRzLz4N.d.ts.map → RouterProvider-CSeAAYD-.d.ts.map} +1 -1
  6. package/dist/cjs/RouterProvider-Ds7y8ZKl.js +2 -0
  7. package/dist/cjs/RouterProvider-Ds7y8ZKl.js.map +1 -0
  8. package/dist/cjs/index.d.ts +2 -2
  9. package/dist/cjs/index.js +1 -1
  10. package/dist/cjs/ink.d.ts +1 -1
  11. package/dist/cjs/ink.js +1 -1
  12. package/dist/cjs/legacy.d.ts +2 -2
  13. package/dist/cjs/legacy.js +1 -1
  14. package/dist/cjs/{useRouterTransition-CjFqNYG6.d.ts → useRouterTransition-BYit_9Mt.d.ts} +11 -1
  15. package/dist/cjs/useRouterTransition-BYit_9Mt.d.ts.map +1 -0
  16. package/dist/esm/Link-B_gDxNSU.mjs +2 -0
  17. package/dist/esm/Link-B_gDxNSU.mjs.map +1 -0
  18. package/dist/esm/RouterProvider-BCE2OtYs.mjs +2 -0
  19. package/dist/esm/RouterProvider-BCE2OtYs.mjs.map +1 -0
  20. package/dist/esm/{RouterProvider-D2wFs7Sd.d.mts → RouterProvider-CtpRoTCZ.d.mts} +3 -3
  21. package/dist/esm/{RouterProvider-D2wFs7Sd.d.mts.map → RouterProvider-CtpRoTCZ.d.mts.map} +1 -1
  22. package/dist/esm/index.d.mts +2 -2
  23. package/dist/esm/index.mjs +1 -1
  24. package/dist/esm/ink.d.mts +1 -1
  25. package/dist/esm/ink.mjs +1 -1
  26. package/dist/esm/legacy.d.mts +2 -2
  27. package/dist/esm/legacy.mjs +1 -1
  28. package/dist/esm/{useRouterTransition-B3UWzXYl.d.mts → useRouterTransition-B65Wvngj.d.mts} +11 -1
  29. package/dist/esm/useRouterTransition-B65Wvngj.d.mts.map +1 -0
  30. package/package.json +5 -5
  31. package/src/components/Link.tsx +23 -4
  32. package/src/hooks/useIsActiveRoute.tsx +15 -5
  33. package/src/types.ts +10 -0
  34. package/dist/cjs/Link-BzzSlRBj.js +0 -2
  35. package/dist/cjs/Link-BzzSlRBj.js.map +0 -1
  36. package/dist/cjs/RouterProvider-XuokeTtL.js +0 -2
  37. package/dist/cjs/RouterProvider-XuokeTtL.js.map +0 -1
  38. package/dist/cjs/useRouterTransition-CjFqNYG6.d.ts.map +0 -1
  39. package/dist/esm/Link-W2dQSXXI.mjs +0 -2
  40. package/dist/esm/Link-W2dQSXXI.mjs.map +0 -1
  41. package/dist/esm/RouterProvider-D84Tsw-s.mjs +0 -2
  42. package/dist/esm/RouterProvider-D84Tsw-s.mjs.map +0 -1
  43. package/dist/esm/useRouterTransition-B3UWzXYl.d.mts.map +0 -1
package/README.md CHANGED
@@ -145,6 +145,18 @@ Navigation link with automatic active state detection. Re-renders only when its
145
145
  </Link>
146
146
  ```
147
147
 
148
+ #### `hash` prop — URL fragment / tab-style UIs
149
+
150
+ ```tsx
151
+ <nav>
152
+ <Link routeName="settings" hash="profile">Profile</Link>
153
+ <Link routeName="settings" hash="account">Account</Link>
154
+ <Link routeName="settings" hash="billing">Billing</Link>
155
+ </nav>
156
+ ```
157
+
158
+ Tri-state: `undefined` preserves the current hash, `""` clears it, a value sets it. Active class is hash-aware — only the matching tab lights up. Live demo: [`examples/web/react/link-hash/`](../../examples/web/react/link-hash/). See the [Hash Fragment Support](https://github.com/greydragon888/real-router/wiki/Hash) wiki page for the full surface.
159
+
148
160
  ### `<RouteView>` (React 19.2+)
149
161
 
150
162
  Declarative route matching with optional `keepAlive` — preserves component state via React's `<Activity>` API.
@@ -0,0 +1,2 @@
1
+ const e=require(`./RouterProvider-Ds7y8ZKl.js`);let t=require(`react`),n=require(`react/jsx-runtime`);function r(t,n){return t.routeName===n.routeName&&t.className===n.className&&t.activeClassName===n.activeClassName&&t.activeStrict===n.activeStrict&&t.ignoreQueryParams===n.ignoreQueryParams&&t.onClick===n.onClick&&t.target===n.target&&t.style===n.style&&t.children===n.children&&t.hash===n.hash&&e.d(t.routeParams,n.routeParams)&&e.d(t.routeOptions,n.routeOptions)}const i=(0,t.memo)(({routeName:r,routeParams:i=e.m,routeOptions:a=e.p,className:o,activeClassName:s=`active`,activeStrict:c=!1,ignoreQueryParams:l=!0,hash:u,onClick:d,target:f,children:p,...m})=>{let h=e.g(),g=e.s(r,i,c,l,u),_=(0,t.useMemo)(()=>e.l(h,r,i,u===void 0?void 0:{hash:u}),[h,r,i,u]),v=(0,t.useCallback)(t=>{d&&(d(t),t.defaultPrevented)||!e.f(t.nativeEvent)||f===`_blank`||(t.preventDefault(),e.u(h,r,i,u,a).catch(()=>{}))},[d,f,h,r,i,a,u]),y=e.c(g,s,o);return(0,n.jsx)(`a`,{...m,href:_,className:y,onClick:v,children:p})},r);i.displayName=`Link`,Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
+ //# sourceMappingURL=Link-C2vVQyg9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Link-C2vVQyg9.js","names":["shallowEqual","EMPTY_PARAMS","EMPTY_OPTIONS","useRouter","useIsActiveRoute","buildHref","shouldNavigate","buildActiveClassName"],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n navigateWithHash,\n shallowEqual,\n} from \"../dom-utils\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { LinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\nfunction areLinkPropsEqual(\n prev: Readonly<LinkProps>,\n next: Readonly<LinkProps>,\n): boolean {\n return (\n prev.routeName === next.routeName &&\n prev.className === next.className &&\n prev.activeClassName === next.activeClassName &&\n prev.activeStrict === next.activeStrict &&\n prev.ignoreQueryParams === next.ignoreQueryParams &&\n prev.onClick === next.onClick &&\n prev.target === next.target &&\n prev.style === next.style &&\n prev.children === next.children &&\n prev.hash === next.hash &&\n shallowEqual(prev.routeParams, next.routeParams) &&\n shallowEqual(prev.routeOptions, next.routeOptions)\n );\n}\n\nconst LinkImpl: FC<LinkProps> = ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n hash,\n onClick,\n target,\n children,\n ...props\n}) => {\n // memo + areLinkPropsEqual guarantees that on bail-out the component does\n // not render; on render, routeParams/routeOptions either changed reference\n // (true change) or comparator failed (e.g., BigInt fallback to identity),\n // so they're safe to use directly in hook deps.\n\n const router = useRouter();\n\n // When `hash` prop is set, active state requires both route AND hash to\n // match (#532). Without this, three tab links sharing routeName=\"settings\"\n // would all be marked active by route-name alone, defeating tab semantics.\n const isActive = useIsActiveRoute(\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n hash,\n );\n\n const href = useMemo(\n () =>\n buildHref(\n router,\n routeName,\n routeParams,\n hash === undefined ? undefined : { hash },\n ),\n [router, routeName, routeParams, hash],\n );\n\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n if (onClick) {\n onClick(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt.nativeEvent) || target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n navigateWithHash(\n router,\n routeName,\n routeParams,\n hash,\n routeOptions,\n ).catch(() => {});\n },\n [onClick, target, router, routeName, routeParams, routeOptions, hash],\n );\n\n const finalClassName = buildActiveClassName(\n isActive,\n activeClassName,\n className,\n );\n\n return (\n <a {...props} href={href} className={finalClassName} onClick={handleClick}>\n {children}\n </a>\n );\n};\n\nexport const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);\n\nLink.displayName = \"Link\";\n"],"mappings":"sGAgBA,SAAS,EACP,EACA,EACS,CACT,OACE,EAAK,YAAc,EAAK,WACxB,EAAK,YAAc,EAAK,WACxB,EAAK,kBAAoB,EAAK,iBAC9B,EAAK,eAAiB,EAAK,cAC3B,EAAK,oBAAsB,EAAK,mBAChC,EAAK,UAAY,EAAK,SACtB,EAAK,SAAW,EAAK,QACrB,EAAK,QAAU,EAAK,OACpB,EAAK,WAAa,EAAK,UACvB,EAAK,OAAS,EAAK,MACnBA,EAAAA,EAAa,EAAK,YAAa,EAAK,YAAY,EAChDA,EAAAA,EAAa,EAAK,aAAc,EAAK,aAAa,CAsFtD,MAAa,GAAA,EAAA,EAAA,OAlFoB,CAC/B,YACA,cAAcC,EAAAA,EACd,eAAeC,EAAAA,EACf,YACA,kBAAkB,SAClB,eAAe,GACf,oBAAoB,GACpB,OACA,UACA,SACA,WACA,GAAG,KACC,CAMJ,IAAM,EAASC,EAAAA,GAAW,CAKpB,EAAWC,EAAAA,EACf,EACA,EACA,EACA,EACA,EACD,CAEK,GAAA,EAAA,EAAA,aAEFC,EAAAA,EACE,EACA,EACA,EACA,IAAS,IAAA,GAAY,IAAA,GAAY,CAAE,OAAM,CAC1C,CACH,CAAC,EAAQ,EAAW,EAAa,EAAK,CACvC,CAEK,GAAA,EAAA,EAAA,aACH,GAAuC,CAClC,IACF,EAAQ,EAAI,CAER,EAAI,mBAKN,CAACC,EAAAA,EAAe,EAAI,YAAY,EAAI,IAAW,WAInD,EAAI,gBAAgB,CACpB,EAAA,EACE,EACA,EACA,EACA,EACA,EACD,CAAC,UAAY,GAAG,GAEnB,CAAC,EAAS,EAAQ,EAAQ,EAAW,EAAa,EAAc,EAAK,CACtE,CAEK,EAAiBC,EAAAA,EACrB,EACA,EACA,EACD,CAED,OACE,EAAA,EAAA,KAAC,IAAD,CAAG,GAAI,EAAa,OAAM,UAAW,EAAgB,QAAS,EAC3D,WACC,CAAA,EAI0C,EAAkB,CAEpE,EAAK,YAAc"}
@@ -1,6 +1,6 @@
1
- import { l as LinkProps } from "./useRouterTransition-CjFqNYG6.js";
1
+ import { l as LinkProps } from "./useRouterTransition-BYit_9Mt.js";
2
2
  import { FC, ReactNode } from "react";
3
- import { Params, Router, State } from "@real-router/core";
3
+ import { NavigationOptions, Params, Router, State } from "@real-router/core";
4
4
 
5
5
  //#region src/components/Link.d.ts
6
6
  declare const Link: FC<LinkProps>;
@@ -24,4 +24,4 @@ interface RouteProviderProps {
24
24
  declare const RouterProvider: FC<RouteProviderProps>;
25
25
  //#endregion
26
26
  export { Link as n, RouterProvider as t };
27
- //# sourceMappingURL=RouterProvider-DIRzLz4N.d.ts.map
27
+ //# sourceMappingURL=RouterProvider-CSeAAYD-.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterProvider-DIRzLz4N.d.ts","names":[],"sources":["../../src/components/Link.tsx","../../../../shared/dom-utils/scroll-restore.ts","../../src/RouterProvider.tsx"],"mappings":";;;;;cAmGa,IAAA,EAAM,EAAA,CAAG,SAAA;;;KCzFV,qBAAA;AAAA,UAEK,wBAAA;EACf,IAAA,GAAO,qBAAA;EACP,eAAA;EACA,eAAA,UAAyB,WAAA;AAAA;;;UCAV,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;EACA,iBAAA,GAAoB,wBAAA;EACpB,eAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
1
+ {"version":3,"file":"RouterProvider-CSeAAYD-.d.ts","names":[],"sources":["../../src/components/Link.tsx","../../../../shared/dom-utils/scroll-restore.ts","../../src/RouterProvider.tsx"],"mappings":";;;;;cAsHa,IAAA,EAAM,EAAA,CAAG,SAAA;;;KC5GV,qBAAA;AAAA,UAEK,wBAAA;EACf,IAAA,GAAO,qBAAA;EACP,eAAA;EACA,eAAA,UAAyB,WAAA;AAAA;;;UCAV,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;EACA,iBAAA,GAAoB,wBAAA;EACpB,eAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
@@ -0,0 +1,2 @@
1
+ let e=require(`react`),t=require(`@real-router/core`),n=require(`@real-router/route-utils`),r=require(`react/jsx-runtime`),i=require(`@real-router/sources`),a=require(`@real-router/core/api`);const o=(0,e.createContext)(null),s=(0,e.createContext)(null),c=(0,e.createContext)(null),l=()=>{let t=(0,e.useContext)(s);if(!t)throw Error(`useRouter must be used within a RouterProvider`);return t};function u(n){let r=l(),a=(0,e.useMemo)(()=>(0,i.createRouteNodeSource)(r,n),[r,n]),o=(0,e.useSyncExternalStore)(a.subscribe,a.getSnapshot,a.getSnapshot),s=(0,t.getNavigator)(r);return(0,e.useMemo)(()=>({navigator:s,route:o.route,previousRoute:o.previousRoute}),[s,o])}const d=Object.freeze({}),f=Object.freeze({}),p=`data-real-router-announcer`;function m(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=h(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),v(t)},f=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,d(e,document.querySelector(`h1`))}},100),p=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=_(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}d(i,t)}})})});return{destroy(){o=!0,p(),clearTimeout(l),clearTimeout(f),g()}}}function h(){let e=document.querySelector(`[${p}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(p,``),document.body.prepend(t),t}function g(){document.querySelector(`[${p}]`)?.remove()}function _(e,t,n,r){if(n)return n(e);let i=(r?.textContent??``).trim(),a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function v(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const y=`real-router:scroll`,b=Object.freeze({destroy:()=>{}});function x(e,t){if(globalThis.window===void 0)return b;let n=t?.mode??`restore`;if(n===`manual`)return b;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let o=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},s=e=>{let t=i?.();t?t.scrollTop=e:globalThis.scrollTo(0,e)},c=e=>{let t=e.context?.url?.hash;if(t!==void 0){if(r&&t.length>0){let e=document.getElementById(t);if(e){e.scrollIntoView();return}}s(0);return}let n=globalThis.location.hash;if(r&&n.length>1){let e;try{e=decodeURIComponent(n.slice(1))}catch{e=n.slice(1)}let t=document.getElementById(e);if(t){t.scrollIntoView();return}}s(0)},l=!1,u=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;t&&w(S(t),o()),requestAnimationFrame(()=>{if(!l){if(n===`top`||!r){c(e);return}if(r.navigationType!==`replace`){if(r.direction===`back`||r.navigationType===`traverse`||r.navigationType===`reload`){s(C()[S(e)]??0);return}c(e)}}})}),d=()=>{let t=e.getState();t&&w(S(t),o())};return globalThis.addEventListener(`pagehide`,d),{destroy:()=>{if(!l){l=!0,u(),globalThis.removeEventListener(`pagehide`,d);try{history.scrollRestoration=a}catch{}}}}}function S(e){return`${e.name}:${T(e.params)}`}function C(){try{let e=sessionStorage.getItem(y);return e?JSON.parse(e):{}}catch{return{}}}function w(e,t){try{let n=C();n[e]=t,sessionStorage.setItem(y,JSON.stringify(n))}catch{}}function T(e){return JSON.stringify(e,E)}function E(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const D=Object.freeze({destroy:()=>{}});function O(e){if(typeof document>`u`||typeof document.startViewTransition!=`function`)return D;let t=null,n=null,r=!1,i=()=>{t?.(),t=null},a=e.subscribeLeave(({signal:e})=>{if(!e.aborted)return r=!1,i(),new Promise(a=>{let o=new Promise(e=>{t=e});e.addEventListener(`abort`,()=>{r||(i(),n?.skipTransition?.(),a())},{once:!0});try{n=document.startViewTransition(()=>(a(),o))}catch{i(),a()}})}),o=e.subscribe(()=>{let e=t;r=!0,t=null,e===null?n=null:setTimeout(()=>{e(),n=null},0)});return{destroy:()=>{a(),o(),n?.skipTransition?.(),n=null,i()}}}function k(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function A(e){return encodeURI(e).replaceAll(`#`,`%23`)}function j(e,t,n,r){try{let i=r?.hash,a;i!==void 0&&(a=i.startsWith(`#`)?i.slice(1):i);let o=e.buildUrl;if(o){let e=o(t,n,a===void 0?void 0:{hash:a});if(e!==void 0)return e}let s=e.buildPath(t,n);return a?`${s}#${A(a)}`:s}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function M(e,t,n,r,i){let a={...i};r!==void 0&&(a.hash=r);let o=e.getState();if(o?.name===t&&F(o.params,n)){let e=o.context?.url?.hash??``;e!==(r??e)&&(a.force=!0,a.hashChange=!0)}return e.navigate(t,n,a)}function N(e){return e?e.match(/\S+/g)??[]:[]}function P(e,t,n){if(e&&t){let e=N(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=N(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function F(e,t){if(Object.is(e,t))return!0;if(!e||!t)return!1;let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;let r=e,i=t;for(let e of n)if(!Object.is(r[e],i[e]))return!1;return!0}function I(t,n,r=!1,a=!0,o){let s=(0,i.createActiveRouteSource)(l(),t,n,o===void 0?{strict:r,ignoreQueryParams:a}:{strict:r,ignoreQueryParams:a,hash:o});return(0,e.useSyncExternalStore)(s.subscribe,s.getSnapshot,s.getSnapshot)}function L({children:t,fallback:n,onError:a}){let o=(0,i.createDismissableError)(l()),s=(0,e.useSyncExternalStore)(o.subscribe,o.getSnapshot,o.getSnapshot),c=(0,e.useRef)(a);return c.current=a,(0,e.useEffect)(()=>{s.error&&c.current?.(s.error,s.toRoute,s.fromRoute)},[s.version]),(0,r.jsxs)(r.Fragment,{children:[t,s.error?n(s.error,s.resetError):null]})}const R=()=>{let t=(0,e.useContext)(c);if(!t)throw Error(`useNavigator must be used within a RouterProvider`);return t},z=()=>(0,n.getRouteUtils)((0,a.getPluginApi)(l()).getTree()),B=()=>{let t=(0,e.useContext)(o);if(!t)throw Error(`useRoute must be used within a RouterProvider`);if(!t.route)throw Error(`useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?`);return t};function V(){let t=(0,i.getTransitionSource)(l());return(0,e.useSyncExternalStore)(t.subscribe,t.getSnapshot,t.getSnapshot)}const H=({router:n,children:a,announceNavigation:l,scrollRestoration:u,viewTransitions:d})=>{(0,e.useEffect)(()=>{if(!l)return;let e=m(n);return()=>{e.destroy()}},[l,n]);let f=u?.mode,p=u?.anchorScrolling,h=u!==void 0;(0,e.useEffect)(()=>{if(!h)return;let e=x(n,{mode:f,anchorScrolling:p,scrollContainer:u.scrollContainer});return()=>{e.destroy()}},[n,h,f,p]),(0,e.useEffect)(()=>{if(!d)return;let e=O(n);return()=>{e.destroy()}},[n,d]);let g=(0,e.useMemo)(()=>(0,t.getNavigator)(n),[n]),_=(0,e.useMemo)(()=>(0,i.createRouteSource)(n),[n]),v=(0,e.useSyncExternalStore)(_.subscribe,_.getSnapshot,_.getSnapshot),y=(0,e.useMemo)(()=>({navigator:g,route:v.route,previousRoute:v.previousRoute}),[g,v]);return(0,r.jsx)(s.Provider,{value:n,children:(0,r.jsx)(c.Provider,{value:g,children:(0,r.jsx)(o.Provider,{value:y,children:a})})})};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return M}});
2
+ //# sourceMappingURL=RouterProvider-Ds7y8ZKl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterProvider-Ds7y8ZKl.js","names":["NOOP_INSTANCE"],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/view-transitions.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouterTransition.tsx","../../src/RouterProvider.tsx"],"sourcesContent":["// packages/react/src/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/src/hooks/useRouter.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = useContext(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const store = useMemo(\n () => createRouteNodeSource(router, nodeName),\n [router, nodeName],\n );\n\n // Use snapshot reference directly. createRouteNodeSource via stabilizeState\n // returns the SAME snapshot when the node-relevant state did not change,\n // so memoization on `[navigator, snapshot]` preserves identity for consumers.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n // getNavigator is WeakMap-cached in core; additional useMemo is redundant.\n const navigator = getNavigator(router);\n\n return useMemo(\n (): RouteContext => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n}\n","// packages/react/src/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","import type { Router, State } from \"@real-router/core\";\n\nconst CLEAR_DELAY = 7000;\nconst SAFARI_READY_DELAY = 100;\nconst ANNOUNCER_ATTR = \"data-real-router-announcer\";\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0\";\n\nexport interface RouteAnnouncerOptions {\n prefix?: string;\n getAnnouncementText?: (route: State) => string;\n}\n\nexport function createRouteAnnouncer(\n router: Router,\n options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n const prefix = options?.prefix ?? \"Navigated to \";\n const getCustomText = options?.getAnnouncementText;\n\n let isInitialNavigation = true;\n let isReady = false;\n let isDestroyed = false;\n let lastAnnouncedText = \"\";\n let pendingText: string | null = null;\n let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const doAnnounce = (text: string, h1: HTMLElement | null): void => {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus(h1);\n };\n\n // Safari-ready delay: announcing before VoiceOver wires up the aria-live region\n // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms\n // before marking the announcer \"ready\" — any navigation during that window is\n // buffered in pendingText and flushed once the delay expires.\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n\n if (pendingText !== null && !isDestroyed) {\n const text = pendingText;\n\n pendingText = null;\n doAnnounce(text, document.querySelector<HTMLElement>(\"h1\"));\n }\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n // Double rAF: waits for two paint frames so the incoming route's DOM\n // (including the new <h1>) is fully rendered before resolveText reads it.\n // Single rAF fires before the new route's template has been attached,\n // which would cause resolveText to pick up the OLD h1 or fall back to\n // document.title / route.name prematurely.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n const text = resolveText(route, prefix, getCustomText, h1);\n\n if (!text || text === lastAnnouncedText) {\n return;\n }\n\n if (!isReady) {\n // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n pendingText = text;\n\n return;\n }\n\n doAnnounce(text, h1);\n });\n });\n });\n\n return {\n destroy() {\n isDestroyed = true;\n unsubscribe();\n clearTimeout(clearTimeoutId);\n clearTimeout(safariTimeoutId);\n removeAnnouncer();\n },\n };\n}\n\nfunction getOrCreateAnnouncer(): HTMLElement {\n const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n if (existing) {\n return existing;\n }\n\n const element = document.createElement(\"div\");\n\n element.setAttribute(\"style\", VISUALLY_HIDDEN);\n element.setAttribute(\"aria-live\", \"assertive\");\n element.setAttribute(\"aria-atomic\", \"true\");\n element.setAttribute(ANNOUNCER_ATTR, \"\");\n\n document.body.prepend(element);\n\n return element;\n}\n\nfunction removeAnnouncer(): void {\n document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();\n}\n\nfunction resolveText(\n route: State,\n prefix: string,\n getCustomText: ((route: State) => string) | undefined,\n h1: HTMLElement | null,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1Text = (h1?.textContent ?? \"\").trim();\n const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n ? \"\"\n : route.name;\n const rawText =\n h1Text || document.title || routeName || globalThis.location.pathname;\n\n return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n if (!h1) {\n return;\n }\n\n if (!h1.hasAttribute(\"tabindex\")) {\n h1.setAttribute(\"tabindex\", \"-1\");\n }\n\n h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst STORAGE_KEY = \"real-router:scroll\";\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"manual\";\n\nexport interface ScrollRestorationOptions {\n mode?: ScrollRestorationMode | undefined;\n anchorScrolling?: boolean | undefined;\n scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\ninterface NavigationContext {\n direction?: \"forward\" | \"back\" | \"unknown\";\n navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n router: Router,\n options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n if (typeof globalThis.window === \"undefined\") {\n return NOOP_INSTANCE;\n }\n\n const mode = options?.mode ?? \"restore\";\n\n // mode \"manual\" = utility does nothing. Don't flip history.scrollRestoration,\n // don't subscribe, don't register pagehide — leave the browser's native\n // auto-restore intact for the app to override if it wants to.\n if (mode === \"manual\") {\n return NOOP_INSTANCE;\n }\n\n const anchorEnabled = options?.anchorScrolling ?? true;\n const getContainer = options?.scrollContainer;\n\n const prevScrollRestoration = history.scrollRestoration;\n\n try {\n history.scrollRestoration = \"manual\";\n } catch {\n // Ignore — some embedded contexts may reject the assignment.\n }\n\n // Resolve the container lazily on every event so containers mounted AFTER\n // the provider still get correct scroll handling. Falls back to window when\n // the getter is absent or returns null (pre-mount).\n const readPos = (): number => {\n const element = getContainer?.();\n\n return element ? element.scrollTop : globalThis.scrollY;\n };\n\n const writePos = (top: number): void => {\n const element = getContainer?.();\n\n if (element) {\n element.scrollTop = top;\n } else {\n globalThis.scrollTo(0, top);\n }\n };\n\n const scrollToHashOrTop = (route: State): void => {\n // URL plugin path (#532): `state.context.url.hash` is the source of truth\n // when one of the URL plugins (browser-plugin / navigation-plugin) is\n // installed. The value is already DECODED — feeding it through\n // `decodeURIComponent` again would throw on a bare `%`.\n const ctxHash = (route.context as { url?: { hash?: string } } | undefined)\n ?.url?.hash;\n\n if (ctxHash !== undefined) {\n if (anchorEnabled && ctxHash.length > 0) {\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(ctxHash);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n\n return;\n }\n\n // Fallback path: no URL plugin, read the DOM. `location.hash` is\n // percent-encoded; ids in the DOM are the raw string, so decode for the\n // match. Fall back to the raw slice if the hash contains a malformed\n // escape sequence (decodeURIComponent throws on those).\n const hash = globalThis.location.hash;\n\n if (anchorEnabled && hash.length > 1) {\n let id: string;\n\n try {\n id = decodeURIComponent(hash.slice(1));\n } catch {\n id = hash.slice(1);\n }\n\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(id);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n };\n\n let destroyed = false;\n\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n const nav = (route.context as { navigation?: NavigationContext })\n .navigation;\n\n // Browsers dispatch reload as the initial navigation after refresh, so\n // previousRoute is undefined and capture is naturally skipped. The\n // pre-refresh position was already persisted via pagehide.\n if (previousRoute) {\n putPos(keyOf(previousRoute), readPos());\n }\n\n // Single rAF so DOM is committed before we read anchors / write scroll.\n // Guard against destroy() racing with the callback.\n requestAnimationFrame(() => {\n if (destroyed) {\n return;\n }\n\n if (mode === \"top\" || !nav) {\n scrollToHashOrTop(route);\n\n return;\n }\n\n if (nav.navigationType === \"replace\") {\n return;\n }\n\n if (\n nav.direction === \"back\" ||\n nav.navigationType === \"traverse\" ||\n nav.navigationType === \"reload\"\n ) {\n writePos(loadStore()[keyOf(route)] ?? 0);\n\n return;\n }\n\n scrollToHashOrTop(route);\n });\n });\n\n const onPageHide = (): void => {\n const current = router.getState();\n\n if (current) {\n putPos(keyOf(current), readPos());\n }\n };\n\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n return {\n destroy: () => {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n unsubscribe();\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n try {\n history.scrollRestoration = prevScrollRestoration;\n } catch {\n // Ignore.\n }\n },\n };\n}\n\nfunction keyOf(state: State): string {\n return `${state.name}:${canonicalJson(state.params)}`;\n}\n\nfunction loadStore(): Record<string, number> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n\n return raw ? (JSON.parse(raw) as Record<string, number>) : {};\n } catch {\n return {};\n }\n}\n\nfunction putPos(key: string, pos: number): void {\n try {\n const store = loadStore();\n\n store[key] = pos;\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // Ignore quota / security errors.\n }\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, canonicalReplacer);\n}\n\nfunction canonicalReplacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable\n const keys = Object.keys(val as Record<string, unknown>).sort(\n (left: string, right: string) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { Router } from \"@real-router/core\";\n\nexport interface ViewTransitions {\n destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ViewTransitions = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport function createViewTransitions(router: Router): ViewTransitions {\n if (\n typeof document === \"undefined\" ||\n typeof document.startViewTransition !== \"function\"\n ) {\n return NOOP_INSTANCE;\n }\n\n let closeVT: (() => void) | null = null;\n let currentVT: { skipTransition?: () => void } | null = null;\n // Tracks whether TRANSITION_SUCCESS fired for the current leave. Used to\n // distinguish \"benign cleanup abort\" (router's async path aborts its own\n // controller in a finally block after successful navigation) from \"real\n // cancellation\" (concurrent navigate, guard rejection, dispose).\n let successFired = false;\n\n const resolveAndClear = (): void => {\n closeVT?.();\n closeVT = null;\n };\n\n const offLeave = router.subscribeLeave(({ signal }) => {\n // Reentrant abort: signal already aborted when we're called. Open no VT\n // — router will fall through to TRANSITION_CANCELLED via isCurrentNav()\n // after leave resolves. addEventListener(\"abort\", ...) does not re-fire\n // for past events, so skipping startViewTransition is the safe path.\n if (signal.aborted) {\n return;\n }\n\n successFired = false;\n resolveAndClear();\n\n // Return a Promise so the router awaits until the browser invokes\n // updateCallback. This ensures old DOM snapshot is captured BEFORE the\n // router commits the new state — giving correct exit→state→entry\n // ordering (vs fire-and-forget, where URL changes before VT captures).\n return new Promise<void>((resolveLeave) => {\n // Capture the resolver synchronously BEFORE startViewTransition() is\n // called. The browser invokes updateCallback in a later task, but\n // router.subscribe (TRANSITION_SUCCESS) can fire before that. If we\n // captured `resolve` inside the callback, subscribe would see closeVT\n // still null and skip resolving — the deferred would hang for 4s\n // until the VT API aborts with TimeoutError.\n const deferred = new Promise<void>((resolve) => {\n closeVT = resolve;\n });\n\n signal.addEventListener(\n \"abort\",\n () => {\n if (successFired) {\n // Router's async path (#finishAsyncNavigation) aborts its own\n // controller in a finally block AFTER completeTransition (and\n // thus AFTER subscribe fired). This is cleanup, not\n // cancellation — VT is progressing normally, do nothing.\n return;\n }\n\n // Real cancellation (concurrent navigate, dispose). Resolve the\n // deferred so updateCallback can complete, skip the VT so no\n // stale animation leaks, and unblock the router if the abort\n // fires before updateCallback was invoked.\n resolveAndClear();\n currentVT?.skipTransition?.();\n resolveLeave();\n },\n { once: true },\n );\n\n try {\n currentVT = document.startViewTransition(() => {\n // Resolving here unblocks the router at the moment the browser\n // enters updateCallback — by spec, old DOM snapshot is captured\n // before this callback runs. Router now proceeds through\n // activation guards and setState; the VT animation waits on\n // `deferred`, which is resolved from router.subscribe after a\n // task-queue tick (see NOTE on setTimeout below).\n resolveLeave();\n\n return deferred;\n });\n } catch {\n // Defensive: spec says startViewTransition doesn't throw under\n // normal conditions, but Chromium has had edge cases (detached\n // document, extension interference). Clean up and unblock router.\n resolveAndClear();\n resolveLeave();\n }\n });\n });\n\n const offSuccess = router.subscribe(() => {\n const resolver = closeVT;\n\n successFired = true;\n closeVT = null;\n\n if (resolver === null) {\n currentVT = null;\n } else {\n // CRITICAL: CANNOT use requestAnimationFrame here. When the router\n // takes the async path (leave returned a Promise), subscribe fires\n // AFTER the browser has already transitioned VT into the\n // \"update-callback-called\" phase. In that phase Chromium sets\n // rendering suppression to true, which ALSO blocks rAF callbacks.\n // rAF would never fire → deferred never resolves → browser aborts\n // vt.ready with TimeoutError after 4s (observed in Chromium).\n //\n // setTimeout runs on the task queue independent of the rendering\n // pipeline, so it fires regardless of suppression. React's scheduler\n // uses MessageChannel tasks, which are queued before our setTimeout,\n // so the new DOM is committed by the time our callback runs.\n setTimeout(() => {\n resolver();\n currentVT = null;\n }, 0);\n }\n });\n\n return {\n destroy: () => {\n offLeave();\n offSuccess();\n currentVT?.skipTransition?.();\n currentVT = null;\n resolveAndClear();\n },\n };\n}\n","import type {\n NavigationOptions,\n Params,\n Router,\n State,\n} from \"@real-router/core\";\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 &&\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\n/**\n * RFC 3986 fragment encoding: preserve sub-delims (`&`, `=`, `?`, `:`),\n * encode space, `%`, control chars, non-ASCII via encodeURI; defensively\n * escape `#` (encodeURI does not). Mirrors `encodeHashFragment` in\n * `shared/browser-env/url-context.ts` — duplicated here because the\n * shared/dom-utils symlink graph does not reach shared/browser-env.\n */\nfunction encodeFragmentInline(decoded: string): string {\n return encodeURI(decoded).replaceAll(\"#\", \"%23\");\n}\n\ntype BuildUrlFn = (\n name: string,\n params: Params,\n options?: { hash?: string },\n) => string | undefined;\n\n/**\n * Builds an href for a `<Link>` element.\n *\n * - Prefers the URL plugin's `buildUrl` (browser-plugin, navigation-plugin,\n * hash-plugin) when present.\n * - Falls back to `router.buildPath` for runtimes without a URL plugin\n * (memory-plugin, console UIs, NativeScript). In that fallback the hash\n * is appended manually so the rendered href is still correct.\n * - The optional 4th argument is an options object so the contract stays\n * extensible. The `hash` option is a decoded fragment without leading \"#\";\n * `<Link hash=\"#section\">` is accepted defensively (leading \"#\" stripped).\n * Frozen API: previous 3-arg call sites continue to work unchanged.\n */\nexport function buildHref(\n router: Router,\n routeName: string,\n routeParams: Params,\n options?: { hash?: string },\n): string | undefined {\n try {\n const rawHash = options?.hash;\n let normHash: string | undefined;\n\n if (rawHash !== undefined) {\n normHash = rawHash.startsWith(\"#\") ? rawHash.slice(1) : rawHash;\n }\n\n const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n if (buildUrl) {\n const url = buildUrl(\n routeName,\n routeParams,\n normHash === undefined ? undefined : { hash: normHash },\n );\n\n if (url !== undefined) {\n return url;\n }\n }\n\n const path = router.buildPath(routeName, routeParams);\n\n return normHash ? `${path}#${encodeFragmentInline(normHash)}` : path;\n } catch {\n console.error(\n `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n );\n\n return undefined;\n }\n}\n\n/**\n * `<Link>` click-handler navigation helper (#532).\n *\n * Wraps `router.navigate(name, params, opts)` with same-route different-hash\n * detection: when the consumer clicks a hash-bearing Link that targets the\n * current route with the same params but a different fragment, core's\n * SAME_STATES check would otherwise reject the navigation. The helper adds\n * `force: true` and `hashChange: true` automatically — subscribers can then\n * disambiguate via `state.context.url.hashChanged`.\n *\n * For pure programmatic same-route hash-only navigation, callers are\n * documented to pass `{ force: true }` themselves; the auto-bypass here is\n * a UX convenience for `<Link hash>` that all 6 framework adapters share.\n */\n/**\n * Local extended-options type. Adapters that depend only on `@real-router/core`\n * (without a URL plugin) do not see the `NavigationOptions` augmentation that\n * declares `hash` / `hashChange`. Casting to this widened type inside the\n * helper keeps shared/dom-utils self-contained — adapters do not need to\n * augment NavigationOptions themselves to consume `<Link hash>`.\n */\ntype HashAwareNavigationOptions = NavigationOptions & {\n hash?: string;\n hashChange?: boolean;\n};\n\nexport function navigateWithHash(\n router: Router,\n routeName: string,\n routeParams: Params,\n hash: string | undefined,\n extraOptions?: NavigationOptions,\n): Promise<State> {\n const opts: HashAwareNavigationOptions = { ...extraOptions };\n\n if (hash !== undefined) {\n opts.hash = hash;\n }\n\n const current = router.getState();\n\n if (\n current?.name === routeName &&\n shallowEqual(current.params, routeParams)\n ) {\n const currentHash =\n (current.context as { url?: { hash?: string } } | undefined)?.url?.hash ??\n \"\";\n const newHash = hash ?? currentHash;\n\n if (currentHash !== newHash) {\n opts.force = true;\n opts.hashChange = true;\n }\n }\n\n return router.navigate(routeName, routeParams, opts);\n}\n\nfunction parseTokens(value: string | undefined): string[] {\n return value ? (value.match(/\\S+/g) ?? []) : [];\n}\n\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n const activeTokens = parseTokens(activeClassName);\n\n if (activeTokens.length === 0) {\n return baseClassName ?? undefined;\n }\n if (!baseClassName) {\n return activeTokens.join(\" \");\n }\n\n const baseTokens = parseTokens(baseClassName);\n const seen = new Set(baseTokens);\n\n for (const token of activeTokens) {\n if (!seen.has(token)) {\n seen.add(token);\n baseTokens.push(token);\n }\n }\n\n return baseTokens.join(\" \");\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function shallowEqual(\n prev: object | undefined,\n next: object | undefined,\n): boolean {\n if (Object.is(prev, next)) {\n return true;\n }\n if (!prev || !next) {\n return false;\n }\n\n const prevKeys = Object.keys(prev);\n\n if (prevKeys.length !== Object.keys(next).length) {\n return false;\n }\n\n const prevRecord = prev as Record<string, unknown>;\n const nextRecord = next as Record<string, unknown>;\n\n for (const key of prevKeys) {\n if (!Object.is(prevRecord[key], nextRecord[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function applyLinkA11y(element: HTMLElement | null | undefined): void {\n if (!element) {\n return;\n }\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.hasAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Params } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n routeName: string,\n params?: Params,\n strict = false,\n ignoreQueryParams = true,\n hash?: string,\n): boolean {\n const router = useRouter();\n\n // createActiveRouteSource is per-router + canonical-args cached in\n // @real-router/sources, so passing params by reference is safe — equivalent\n // param shapes hit the same cache entry regardless of key order. The\n // `hash` argument (#532) is part of the cache key when defined: a Link\n // pointing to `/settings#account` shares its source only with other\n // consumers using the same routeName + params + hash.\n // exactOptionalPropertyTypes forbids `{ hash: undefined }` literally, so\n // we conditionally include the key only when the caller passed a value.\n const store = createActiveRouteSource(\n router,\n routeName,\n params,\n hash === undefined\n ? { strict, ignoreQueryParams }\n : { strict, ignoreQueryParams, hash },\n );\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { createDismissableError } from \"@real-router/sources\";\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { ReactNode, JSX } from \"react\";\n\nexport interface RouterErrorBoundaryProps {\n readonly children: ReactNode;\n readonly fallback: (error: RouterError, resetError: () => void) => ReactNode;\n readonly onError?: (\n error: RouterError,\n toRoute: State | null,\n fromRoute: State | null,\n ) => void;\n}\n\nexport function RouterErrorBoundary({\n children,\n fallback,\n onError,\n}: RouterErrorBoundaryProps): JSX.Element {\n const router = useRouter();\n const store = createDismissableError(router);\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n\n const onErrorRef = useRef(onError);\n\n // eslint-disable-next-line @eslint-react/refs -- \"latest ref\" pattern: sync callback to ref to avoid effect re-runs\n onErrorRef.current = onError;\n\n useEffect(() => {\n if (snapshot.error) {\n onErrorRef.current?.(\n snapshot.error,\n snapshot.toRoute,\n snapshot.fromRoute,\n );\n }\n // eslint-disable-next-line @eslint-react/exhaustive-deps -- onError tracked via ref, snapshot fields accessed inside callback\n }, [snapshot.version]);\n\n return (\n <>\n {children}\n {snapshot.error ? fallback(snapshot.error, snapshot.resetError) : null}\n </>\n );\n}\n","// packages/react/src/hooks/useNavigator.tsx\n\nimport { useContext } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = useContext(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getPluginApi } from \"@real-router/core/api\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside\n * `@real-router/route-utils`, so the same router always returns the same\n * `RouteUtils` instance across renders — no local cache needed in the adapter.\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n","// packages/react/src/hooks/useRoute.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\nimport type { Params, State } from \"@real-router/core\";\n\nexport const useRoute = <P extends Params = Params>(): Omit<\n RouteContextType<P>,\n \"route\"\n> & { route: State<P> } => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouterProvider\");\n }\n\n if (!routeContext.route) {\n throw new Error(\n \"useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?\",\n );\n }\n\n return routeContext as Omit<RouteContextType<P>, \"route\"> & {\n route: State<P>;\n };\n};\n","import { getTransitionSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\n\nexport function useRouterTransition(): RouterTransitionSnapshot {\n const router = useRouter();\n const store = getTransitionSource(router);\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { useEffect, useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\nimport {\n createRouteAnnouncer,\n createScrollRestoration,\n createViewTransitions,\n} from \"./dom-utils\";\n\nimport type { ScrollRestorationOptions } from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n announceNavigation?: boolean;\n scrollRestoration?: ScrollRestorationOptions;\n viewTransitions?: boolean;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\n scrollRestoration,\n viewTransitions,\n}) => {\n useEffect(() => {\n if (!announceNavigation) {\n return;\n }\n\n const announcer = createRouteAnnouncer(router);\n\n return () => {\n announcer.destroy();\n };\n }, [announceNavigation, router]);\n\n // Primitive deps so inline `{ mode: \"restore\" }` doesn't thrash on every\n // render. scrollContainer is a getter invoked lazily on every event inside\n // the utility — swapping its reference doesn't change the resolved element,\n // so we intentionally omit it from deps to keep inline getters stable.\n const srMode = scrollRestoration?.mode;\n const srAnchor = scrollRestoration?.anchorScrolling;\n const srEnabled = scrollRestoration !== undefined;\n\n useEffect(() => {\n if (!srEnabled) {\n return;\n }\n\n const sr = createScrollRestoration(router, {\n mode: srMode,\n anchorScrolling: srAnchor,\n // srEnabled check above guarantees scrollRestoration is defined.\n scrollContainer: scrollRestoration.scrollContainer,\n });\n\n return () => {\n sr.destroy();\n };\n // scrollRestoration (for scrollContainer) omitted — see comment above.\n // eslint-disable-next-line @eslint-react/exhaustive-deps\n }, [router, srEnabled, srMode, srAnchor]);\n\n useEffect(() => {\n if (!viewTransitions) {\n return;\n }\n\n const vt = createViewTransitions(router);\n\n return () => {\n vt.destroy();\n };\n }, [router, viewTransitions]);\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // useSyncExternalStore manages the router subscription lifecycle:\n // subscribe connects to router on first listener, unsubscribes on last.\n // This is Strict Mode safe — no useEffect cleanup needed.\n const store = useMemo(() => createRouteSource(router), [router]);\n // Use snapshot reference directly. createRouteSource via stabilizeState\n // returns the SAME snapshot reference when route.path is unchanged, so\n // useMemo below sees stable deps for idempotent navigations and\n // RouteContext consumers do not re-render.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const routeContextValue = useMemo(\n () => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n\n return (\n <RouterContext.Provider value={router}>\n <NavigatorContext.Provider value={navigator}>\n <RouteContext.Provider value={routeContextValue}>\n {children}\n </RouteContext.Provider>\n </NavigatorContext.Provider>\n </RouterContext.Provider>\n );\n};\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-CjFqNYG6.js";
2
- import { n as Link, t as RouterProvider } from "./RouterProvider-DIRzLz4N.js";
1
+ import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-BYit_9Mt.js";
2
+ import { n as Link, t as RouterProvider } from "./RouterProvider-CSeAAYD-.js";
3
3
  import { ReactElement, ReactNode } from "react";
4
4
  import { Navigator, State } from "@real-router/core";
5
5
  import { RouterTransitionSnapshot } from "@real-router/sources";
package/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-XuokeTtL.js`),t=require(`./Link-BzzSlRBj.js`);let n=require(`react`),r=require(`@real-router/core`),i=require(`@real-router/route-utils`),a=require(`react/jsx-runtime`);function o(e){return null}o.displayName=`RouteView.Match`;function s(e){return null}s.displayName=`RouteView.Self`;function c(e){return null}c.displayName=`RouteView.NotFound`;function l(e,t,n){return t===``?!1:n?e===t:(0,i.startsWithSegment)(e,t)}function u(e,t){for(let r of n.Children.toArray(e))(0,n.isValidElement)(r)&&(r.type===o||r.type===s||r.type===c?t.push(r):u(r.props.children,t))}function d(e,t,r,i,o){let s=o===void 0?e:(0,a.jsx)(n.Suspense,{fallback:o,children:e});return r?(0,a.jsx)(n.Activity,{mode:i,children:s},t):(0,a.jsx)(n.Fragment,{children:s},t)}function f(e,t){return e.type===c?(t.notFoundChildren=e.props.children,!0):e.type===s?(t.selfFound||=(t.selfChildren=e.props.children,t.selfFallback=e.props.fallback,!0),!0):!1}function p(e,t,n,r,i){let{segment:a,exact:o=!1,keepAlive:s=!1,fallback:c}=e.props,u=n?`${n}.${a}`:a;return!i&&l(t,u,o)?(r.add(u),{rendered:d(e.props.children,u,s,`visible`,c),matched:!0}):s&&r.has(u)?{rendered:d(e.props.children,u,s,`hidden`,c),matched:!1}:{rendered:null,matched:!1}}function m(e,t,i,o){if(o.selfFound&&t===i){e.push(d(o.selfChildren,`__route-view-self__`,!1,`visible`,o.selfFallback));return}t===r.UNKNOWN_ROUTE&&o.notFoundChildren!==null&&e.push((0,a.jsx)(n.Fragment,{children:o.notFoundChildren},`__route-view-not-found__`))}function h(e,t,n,r){let i={selfChildren:null,selfFallback:void 0,selfFound:!1,notFoundChildren:null},a=!1,o=[];for(let s of e){if(f(s,i))continue;let e=p(s,t,n,r,a);e.matched&&(a=!0),e.rendered!==null&&o.push(e.rendered)}return a||m(o,t,n,i),{rendered:o,activeMatchFound:a}}function g({nodeName:t,children:r}){let{route:i}=e.m(t),o=(0,n.useRef)(null);o.current??=new Set;let s=(0,n.useMemo)(()=>{let e=[];return u(r,e),e},[r]);if(!i)return null;let{rendered:c}=h(s,i.name,t,o.current);return c.length>0?(0,a.jsx)(a.Fragment,{children:c}):null}g.displayName=`RouteView`;const _=Object.assign(g,{Match:o,Self:s,NotFound:c});function v(t,r){let i=e.h(),a=(0,n.useRef)(t),o=r?.skipSameRoute??!0;(0,n.useLayoutEffect)(()=>{a.current=t}),(0,n.useEffect)(()=>i.subscribeLeave(({route:e,nextRoute:t,signal:n})=>{if(!(o&&e.name===t.name)&&!n.aborted)return a.current({route:e,nextRoute:t,signal:n})}),[i,o])}function y(t,r){let{route:i,previousRoute:a}=e.r(),o=(0,n.useRef)(t),s=(0,n.useRef)(null),c=r?.skipSameRoute??!0;(0,n.useLayoutEffect)(()=>{o.current=t}),(0,n.useEffect)(()=>{i.transition.from&&(c&&i.transition.from===i.name||s.current===i||!a||(s.current=i,o.current({route:i,previousRoute:a})))},[i,a,c])}exports.Link=t.t,exports.RouteView=_,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteEnter=y,exports.useRouteExit=v,exports.useRouteNode=e.m,exports.useRouteUtils=e.i,exports.useRouter=e.h,exports.useRouterTransition=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-Ds7y8ZKl.js`),t=require(`./Link-C2vVQyg9.js`);let n=require(`react`),r=require(`@real-router/core`),i=require(`@real-router/route-utils`),a=require(`react/jsx-runtime`);function o(e){return null}o.displayName=`RouteView.Match`;function s(e){return null}s.displayName=`RouteView.Self`;function c(e){return null}c.displayName=`RouteView.NotFound`;function l(e,t,n){return t===``?!1:n?e===t:(0,i.startsWithSegment)(e,t)}function u(e,t){for(let r of n.Children.toArray(e))(0,n.isValidElement)(r)&&(r.type===o||r.type===s||r.type===c?t.push(r):u(r.props.children,t))}function d(e,t,r,i,o){let s=o===void 0?e:(0,a.jsx)(n.Suspense,{fallback:o,children:e});return r?(0,a.jsx)(n.Activity,{mode:i,children:s},t):(0,a.jsx)(n.Fragment,{children:s},t)}function f(e,t){return e.type===c?(t.notFoundChildren=e.props.children,!0):e.type===s?(t.selfFound||=(t.selfChildren=e.props.children,t.selfFallback=e.props.fallback,!0),!0):!1}function p(e,t,n,r,i){let{segment:a,exact:o=!1,keepAlive:s=!1,fallback:c}=e.props,u=n?`${n}.${a}`:a;return!i&&l(t,u,o)?(r.add(u),{rendered:d(e.props.children,u,s,`visible`,c),matched:!0}):s&&r.has(u)?{rendered:d(e.props.children,u,s,`hidden`,c),matched:!1}:{rendered:null,matched:!1}}function m(e,t,i,o){if(o.selfFound&&t===i){e.push(d(o.selfChildren,`__route-view-self__`,!1,`visible`,o.selfFallback));return}t===r.UNKNOWN_ROUTE&&o.notFoundChildren!==null&&e.push((0,a.jsx)(n.Fragment,{children:o.notFoundChildren},`__route-view-not-found__`))}function h(e,t,n,r){let i={selfChildren:null,selfFallback:void 0,selfFound:!1,notFoundChildren:null},a=!1,o=[];for(let s of e){if(f(s,i))continue;let e=p(s,t,n,r,a);e.matched&&(a=!0),e.rendered!==null&&o.push(e.rendered)}return a||m(o,t,n,i),{rendered:o,activeMatchFound:a}}function g({nodeName:t,children:r}){let{route:i}=e.h(t),o=(0,n.useRef)(null);o.current??=new Set;let s=(0,n.useMemo)(()=>{let e=[];return u(r,e),e},[r]);if(!i)return null;let{rendered:c}=h(s,i.name,t,o.current);return c.length>0?(0,a.jsx)(a.Fragment,{children:c}):null}g.displayName=`RouteView`;const _=Object.assign(g,{Match:o,Self:s,NotFound:c});function v(t,r){let i=e.g(),a=(0,n.useRef)(t),o=r?.skipSameRoute??!0;(0,n.useLayoutEffect)(()=>{a.current=t}),(0,n.useEffect)(()=>i.subscribeLeave(({route:e,nextRoute:t,signal:n})=>{if(!(o&&e.name===t.name)&&!n.aborted)return a.current({route:e,nextRoute:t,signal:n})}),[i,o])}function y(t,r){let{route:i,previousRoute:a}=e.r(),o=(0,n.useRef)(t),s=(0,n.useRef)(null),c=r?.skipSameRoute??!0;(0,n.useLayoutEffect)(()=>{o.current=t}),(0,n.useEffect)(()=>{i.transition.from&&(c&&i.transition.from===i.name||s.current===i||!a||(s.current=i,o.current({route:i,previousRoute:a})))},[i,a,c])}exports.Link=t.t,exports.RouteView=_,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteEnter=y,exports.useRouteExit=v,exports.useRouteNode=e.h,exports.useRouteUtils=e.i,exports.useRouter=e.g,exports.useRouterTransition=e.n;
2
2
  //# sourceMappingURL=index.js.map
package/dist/cjs/ink.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-CjFqNYG6.js";
1
+ import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-BYit_9Mt.js";
2
2
  import { FC, ReactNode } from "react";
3
3
  import { NavigationOptions, Navigator, Params, Router } from "@real-router/core";
4
4
  import { RouterTransitionSnapshot } from "@real-router/sources";
package/dist/cjs/ink.js CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-XuokeTtL.js`);let t=require(`react`),n=require(`react/jsx-runtime`),r=require(`ink`);function i(t,n){return t.routeName===n.routeName&&t.activeStrict===n.activeStrict&&t.ignoreQueryParams===n.ignoreQueryParams&&t.color===n.color&&t.activeColor===n.activeColor&&t.focusColor===n.focusColor&&t.inverse===n.inverse&&t.activeInverse===n.activeInverse&&t.focusInverse===n.focusInverse&&t.id===n.id&&t.autoFocus===n.autoFocus&&t.onSelect===n.onSelect&&t.children===n.children&&e.u(t.routeParams,n.routeParams)&&e.u(t.routeOptions,n.routeOptions)}const a=(0,t.memo)(({routeName:i,routeParams:a=e.p,routeOptions:o=e.f,activeStrict:s=!1,ignoreQueryParams:c=!0,color:l,activeColor:u,focusColor:d,inverse:f,activeInverse:p,focusInverse:m,id:h,autoFocus:g,onSelect:_,children:v})=>{let y=e.h(),{isFocused:b}=(0,r.useFocus)({...h!==void 0&&{id:h},...g!==void 0&&{autoFocus:g}}),x=e.s(i,a,s,c),S=(0,t.useCallback)(()=>{_?.(),y.navigate(i,a,o).catch(()=>{})},[_,y,i,a,o]);(0,r.useInput)((e,t)=>{t.return&&S()},{isActive:b});let C=l;b?C=d??u??l:x&&(C=u??l);let w=f;b?w=m??p??f:x&&(w=p??f);let T={};return C!==void 0&&(T.color=C),w!==void 0&&(T.inverse=w),(0,n.jsx)(r.Text,{...T,children:v})},i);a.displayName=`InkLink`;const o=({router:t,children:r})=>(0,n.jsx)(e.t,{router:t,children:r});exports.InkLink=a,exports.InkRouterProvider=o,exports.RouterErrorBoundary=e.o,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.m,exports.useRouteUtils=e.i,exports.useRouter=e.h,exports.useRouterTransition=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-Ds7y8ZKl.js`);let t=require(`react`),n=require(`react/jsx-runtime`),r=require(`ink`);function i(t,n){return t.routeName===n.routeName&&t.activeStrict===n.activeStrict&&t.ignoreQueryParams===n.ignoreQueryParams&&t.color===n.color&&t.activeColor===n.activeColor&&t.focusColor===n.focusColor&&t.inverse===n.inverse&&t.activeInverse===n.activeInverse&&t.focusInverse===n.focusInverse&&t.id===n.id&&t.autoFocus===n.autoFocus&&t.onSelect===n.onSelect&&t.children===n.children&&e.d(t.routeParams,n.routeParams)&&e.d(t.routeOptions,n.routeOptions)}const a=(0,t.memo)(({routeName:i,routeParams:a=e.m,routeOptions:o=e.p,activeStrict:s=!1,ignoreQueryParams:c=!0,color:l,activeColor:u,focusColor:d,inverse:f,activeInverse:p,focusInverse:m,id:h,autoFocus:g,onSelect:_,children:v})=>{let y=e.g(),{isFocused:b}=(0,r.useFocus)({...h!==void 0&&{id:h},...g!==void 0&&{autoFocus:g}}),x=e.s(i,a,s,c),S=(0,t.useCallback)(()=>{_?.(),y.navigate(i,a,o).catch(()=>{})},[_,y,i,a,o]);(0,r.useInput)((e,t)=>{t.return&&S()},{isActive:b});let C=l;b?C=d??u??l:x&&(C=u??l);let w=f;b?w=m??p??f:x&&(w=p??f);let T={};return C!==void 0&&(T.color=C),w!==void 0&&(T.inverse=w),(0,n.jsx)(r.Text,{...T,children:v})},i);a.displayName=`InkLink`;const o=({router:t,children:r})=>(0,n.jsx)(e.t,{router:t,children:r});exports.InkLink=a,exports.InkRouterProvider=o,exports.RouterErrorBoundary=e.o,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.h,exports.useRouteUtils=e.i,exports.useRouter=e.g,exports.useRouterTransition=e.n;
2
2
  //# sourceMappingURL=ink.js.map
@@ -1,5 +1,5 @@
1
- import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-CjFqNYG6.js";
2
- import { n as Link, t as RouterProvider } from "./RouterProvider-DIRzLz4N.js";
1
+ import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-BYit_9Mt.js";
2
+ import { n as Link, t as RouterProvider } from "./RouterProvider-CSeAAYD-.js";
3
3
  import { Navigator } from "@real-router/core";
4
4
  import { RouterTransitionSnapshot } from "@real-router/sources";
5
5
  export { Link, type LinkProps, type Navigator, RouterErrorBoundary, type RouterErrorBoundaryProps, RouterProvider, type RouterTransitionSnapshot, useNavigator, useRoute, useRouteNode, useRouteUtils, useRouter, useRouterTransition };
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-XuokeTtL.js`),t=require(`./Link-BzzSlRBj.js`);exports.Link=t.t,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.m,exports.useRouteUtils=e.i,exports.useRouter=e.h,exports.useRouterTransition=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-Ds7y8ZKl.js`),t=require(`./Link-C2vVQyg9.js`);exports.Link=t.t,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.h,exports.useRouteUtils=e.i,exports.useRouter=e.g,exports.useRouterTransition=e.n;
@@ -18,6 +18,16 @@ interface LinkProps<P extends Params = Params> extends HTMLAttributes<HTMLAnchor
18
18
  activeClassName?: string;
19
19
  activeStrict?: boolean;
20
20
  ignoreQueryParams?: boolean;
21
+ /**
22
+ * URL fragment (decoded form, no leading "#") (#532).
23
+ * - omitted/`undefined` → preserve current fragment on same-route navigation
24
+ * - `""` → clear fragment
25
+ * - non-empty → set fragment
26
+ *
27
+ * Requires a URL plugin (browser-plugin or navigation-plugin) for full
28
+ * round-trip; hash-plugin ignores the prop with a one-time dev warning.
29
+ */
30
+ hash?: string;
21
31
  target?: string;
22
32
  onClick?: MouseEventHandler<HTMLAnchorElement>;
23
33
  onMouseOver?: MouseEventHandler<HTMLAnchorElement>;
@@ -79,4 +89,4 @@ declare function useRouteNode(nodeName: string): RouteContext;
79
89
  declare function useRouterTransition(): RouterTransitionSnapshot;
80
90
  //#endregion
81
91
  export { useNavigator as a, RouterErrorBoundaryProps as c, useRouteUtils as i, LinkProps as l, useRouteNode as n, useRouter as o, useRoute as r, RouterErrorBoundary as s, useRouterTransition as t };
82
- //# sourceMappingURL=useRouterTransition-CjFqNYG6.d.ts.map
92
+ //# sourceMappingURL=useRouterTransition-BYit_9Mt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRouterTransition-BYit_9Mt.d.ts","names":[],"sources":["../../src/types.ts","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useRouter.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useRouterTransition.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA,WAAuB,MAAA,GAAS,MAAA;EAC1C,SAAA,EAAW,SAAA;AAAA,IACT,UAAA,CAAW,CAAA;AAAA,UAEE,SAAA,WACL,MAAA,GAAS,MAAA,UACX,cAAA,CAAe,iBAAA;EACvB,SAAA;EACA,WAAA,GAAc,CAAA;EACd,YAAA,GAAe,iBAAA;EACf,eAAA;EACA,YAAA;EACA,iBAAA;EAjB0B;;;;;;;;;EA2B1B,IAAA;EACA,MAAA;EACA,OAAA,GAAU,iBAAA,CAAkB,iBAAA;EAC5B,WAAA,GAAc,iBAAA,CAAkB,iBAAA;AAAA;;;UC9BjB,wBAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,KAAA,EAAO,WAAA,EAAa,UAAA,iBAA2B,SAAA;EAAA,SAC1D,OAAA,IACP,KAAA,EAAO,WAAA,EACP,OAAA,EAAS,KAAA,SACT,SAAA,EAAW,KAAA;AAAA;AAAA,iBAIC,mBAAA,CAAA;EACd,QAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,GAAA,CAAI,OAAA;;;cCdrB,SAAA,QAAgB,MAAA;;;cCAhB,YAAA,QAAmB,SAAA;;;;;;;;AHAhC;;;;;;;;;;;;;;;;;;cIsBa,aAAA,QAAoB,UAAA;;;cCrBpB,QAAA,aAAsB,MAAA,GAAS,MAAA,OAAW,IAAA,CACrD,YAAA,CAAiB,CAAA;EAEb,KAAA,EAAO,KAAA,CAAM,CAAA;AAAA;;;iBCJH,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA"}
@@ -0,0 +1,2 @@
1
+ import{c as e,d as t,f as n,g as r,l as i,m as a,p as o,s,u as c}from"./RouterProvider-BCE2OtYs.mjs";import{memo as l,useCallback as u,useMemo as d}from"react";import{jsx as f}from"react/jsx-runtime";function p(e,n){return e.routeName===n.routeName&&e.className===n.className&&e.activeClassName===n.activeClassName&&e.activeStrict===n.activeStrict&&e.ignoreQueryParams===n.ignoreQueryParams&&e.onClick===n.onClick&&e.target===n.target&&e.style===n.style&&e.children===n.children&&e.hash===n.hash&&t(e.routeParams,n.routeParams)&&t(e.routeOptions,n.routeOptions)}const m=l(({routeName:t,routeParams:l=a,routeOptions:p=o,className:m,activeClassName:h=`active`,activeStrict:g=!1,ignoreQueryParams:_=!0,hash:v,onClick:y,target:b,children:x,...S})=>{let C=r(),w=s(t,l,g,_,v),T=d(()=>i(C,t,l,v===void 0?void 0:{hash:v}),[C,t,l,v]),E=u(e=>{y&&(y(e),e.defaultPrevented)||!n(e.nativeEvent)||b===`_blank`||(e.preventDefault(),c(C,t,l,v,p).catch(()=>{}))},[y,b,C,t,l,p,v]),D=e(w,h,m);return f(`a`,{...S,href:T,className:D,onClick:E,children:x})},p);m.displayName=`Link`;export{m as t};
2
+ //# sourceMappingURL=Link-B_gDxNSU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Link-B_gDxNSU.mjs","names":[],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n navigateWithHash,\n shallowEqual,\n} from \"../dom-utils\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { LinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\nfunction areLinkPropsEqual(\n prev: Readonly<LinkProps>,\n next: Readonly<LinkProps>,\n): boolean {\n return (\n prev.routeName === next.routeName &&\n prev.className === next.className &&\n prev.activeClassName === next.activeClassName &&\n prev.activeStrict === next.activeStrict &&\n prev.ignoreQueryParams === next.ignoreQueryParams &&\n prev.onClick === next.onClick &&\n prev.target === next.target &&\n prev.style === next.style &&\n prev.children === next.children &&\n prev.hash === next.hash &&\n shallowEqual(prev.routeParams, next.routeParams) &&\n shallowEqual(prev.routeOptions, next.routeOptions)\n );\n}\n\nconst LinkImpl: FC<LinkProps> = ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n hash,\n onClick,\n target,\n children,\n ...props\n}) => {\n // memo + areLinkPropsEqual guarantees that on bail-out the component does\n // not render; on render, routeParams/routeOptions either changed reference\n // (true change) or comparator failed (e.g., BigInt fallback to identity),\n // so they're safe to use directly in hook deps.\n\n const router = useRouter();\n\n // When `hash` prop is set, active state requires both route AND hash to\n // match (#532). Without this, three tab links sharing routeName=\"settings\"\n // would all be marked active by route-name alone, defeating tab semantics.\n const isActive = useIsActiveRoute(\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n hash,\n );\n\n const href = useMemo(\n () =>\n buildHref(\n router,\n routeName,\n routeParams,\n hash === undefined ? undefined : { hash },\n ),\n [router, routeName, routeParams, hash],\n );\n\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n if (onClick) {\n onClick(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt.nativeEvent) || target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n navigateWithHash(\n router,\n routeName,\n routeParams,\n hash,\n routeOptions,\n ).catch(() => {});\n },\n [onClick, target, router, routeName, routeParams, routeOptions, hash],\n );\n\n const finalClassName = buildActiveClassName(\n isActive,\n activeClassName,\n className,\n );\n\n return (\n <a {...props} href={href} className={finalClassName} onClick={handleClick}>\n {children}\n </a>\n );\n};\n\nexport const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);\n\nLink.displayName = \"Link\";\n"],"mappings":"wMAgBA,SAAS,EACP,EACA,EACS,CACT,OACE,EAAK,YAAc,EAAK,WACxB,EAAK,YAAc,EAAK,WACxB,EAAK,kBAAoB,EAAK,iBAC9B,EAAK,eAAiB,EAAK,cAC3B,EAAK,oBAAsB,EAAK,mBAChC,EAAK,UAAY,EAAK,SACtB,EAAK,SAAW,EAAK,QACrB,EAAK,QAAU,EAAK,OACpB,EAAK,WAAa,EAAK,UACvB,EAAK,OAAS,EAAK,MACnB,EAAa,EAAK,YAAa,EAAK,YAAY,EAChD,EAAa,EAAK,aAAc,EAAK,aAAa,CAsFtD,MAAa,EAAsB,GAlFF,CAC/B,YACA,cAAc,EACd,eAAe,EACf,YACA,kBAAkB,SAClB,eAAe,GACf,oBAAoB,GACpB,OACA,UACA,SACA,WACA,GAAG,KACC,CAMJ,IAAM,EAAS,GAAW,CAKpB,EAAW,EACf,EACA,EACA,EACA,EACA,EACD,CAEK,EAAO,MAET,EACE,EACA,EACA,EACA,IAAS,IAAA,GAAY,IAAA,GAAY,CAAE,OAAM,CAC1C,CACH,CAAC,EAAQ,EAAW,EAAa,EAAK,CACvC,CAEK,EAAc,EACjB,GAAuC,CAClC,IACF,EAAQ,EAAI,CAER,EAAI,mBAKN,CAAC,EAAe,EAAI,YAAY,EAAI,IAAW,WAInD,EAAI,gBAAgB,CACpB,EACE,EACA,EACA,EACA,EACA,EACD,CAAC,UAAY,GAAG,GAEnB,CAAC,EAAS,EAAQ,EAAQ,EAAW,EAAa,EAAc,EAAK,CACtE,CAEK,EAAiB,EACrB,EACA,EACA,EACD,CAED,OACE,EAAC,IAAD,CAAG,GAAI,EAAa,OAAM,UAAW,EAAgB,QAAS,EAC3D,WACC,CAAA,EAI0C,EAAkB,CAEpE,EAAK,YAAc"}
@@ -0,0 +1,2 @@
1
+ import{createContext as e,useContext as t,useEffect as n,useMemo as r,useRef as i,useSyncExternalStore as a}from"react";import{getNavigator as o}from"@real-router/core";import{getRouteUtils as s}from"@real-router/route-utils";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import{createActiveRouteSource as d,createDismissableError as f,createRouteNodeSource as p,createRouteSource as m,getTransitionSource as h}from"@real-router/sources";import{getPluginApi as g}from"@real-router/core/api";const _=e(null),v=e(null),y=e(null),b=()=>{let e=t(v);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e};function x(e){let t=b(),n=r(()=>p(t,e),[t,e]),i=a(n.subscribe,n.getSnapshot,n.getSnapshot),s=o(t);return r(()=>({navigator:s,route:i.route,previousRoute:i.previousRoute}),[s,i])}const S=Object.freeze({}),C=Object.freeze({}),w=`data-real-router-announcer`;function T(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=E(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),k(t)},f=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,d(e,document.querySelector(`h1`))}},100),p=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=O(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}d(i,t)}})})});return{destroy(){o=!0,p(),clearTimeout(l),clearTimeout(f),D()}}}function E(){let e=document.querySelector(`[${w}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(w,``),document.body.prepend(t),t}function D(){document.querySelector(`[${w}]`)?.remove()}function O(e,t,n,r){if(n)return n(e);let i=(r?.textContent??``).trim(),a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function k(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const A=`real-router:scroll`,j=Object.freeze({destroy:()=>{}});function M(e,t){if(globalThis.window===void 0)return j;let n=t?.mode??`restore`;if(n===`manual`)return j;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let o=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},s=e=>{let t=i?.();t?t.scrollTop=e:globalThis.scrollTo(0,e)},c=e=>{let t=e.context?.url?.hash;if(t!==void 0){if(r&&t.length>0){let e=document.getElementById(t);if(e){e.scrollIntoView();return}}s(0);return}let n=globalThis.location.hash;if(r&&n.length>1){let e;try{e=decodeURIComponent(n.slice(1))}catch{e=n.slice(1)}let t=document.getElementById(e);if(t){t.scrollIntoView();return}}s(0)},l=!1,u=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;t&&F(N(t),o()),requestAnimationFrame(()=>{if(!l){if(n===`top`||!r){c(e);return}if(r.navigationType!==`replace`){if(r.direction===`back`||r.navigationType===`traverse`||r.navigationType===`reload`){s(P()[N(e)]??0);return}c(e)}}})}),d=()=>{let t=e.getState();t&&F(N(t),o())};return globalThis.addEventListener(`pagehide`,d),{destroy:()=>{if(!l){l=!0,u(),globalThis.removeEventListener(`pagehide`,d);try{history.scrollRestoration=a}catch{}}}}}function N(e){return`${e.name}:${I(e.params)}`}function P(){try{let e=sessionStorage.getItem(A);return e?JSON.parse(e):{}}catch{return{}}}function F(e,t){try{let n=P();n[e]=t,sessionStorage.setItem(A,JSON.stringify(n))}catch{}}function I(e){return JSON.stringify(e,L)}function L(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const R=Object.freeze({destroy:()=>{}});function z(e){if(typeof document>`u`||typeof document.startViewTransition!=`function`)return R;let t=null,n=null,r=!1,i=()=>{t?.(),t=null},a=e.subscribeLeave(({signal:e})=>{if(!e.aborted)return r=!1,i(),new Promise(a=>{let o=new Promise(e=>{t=e});e.addEventListener(`abort`,()=>{r||(i(),n?.skipTransition?.(),a())},{once:!0});try{n=document.startViewTransition(()=>(a(),o))}catch{i(),a()}})}),o=e.subscribe(()=>{let e=t;r=!0,t=null,e===null?n=null:setTimeout(()=>{e(),n=null},0)});return{destroy:()=>{a(),o(),n?.skipTransition?.(),n=null,i()}}}function B(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function V(e){return encodeURI(e).replaceAll(`#`,`%23`)}function H(e,t,n,r){try{let i=r?.hash,a;i!==void 0&&(a=i.startsWith(`#`)?i.slice(1):i);let o=e.buildUrl;if(o){let e=o(t,n,a===void 0?void 0:{hash:a});if(e!==void 0)return e}let s=e.buildPath(t,n);return a?`${s}#${V(a)}`:s}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function U(e,t,n,r,i){let a={...i};r!==void 0&&(a.hash=r);let o=e.getState();if(o?.name===t&&K(o.params,n)){let e=o.context?.url?.hash??``;e!==(r??e)&&(a.force=!0,a.hashChange=!0)}return e.navigate(t,n,a)}function W(e){return e?e.match(/\S+/g)??[]:[]}function G(e,t,n){if(e&&t){let e=W(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=W(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function K(e,t){if(Object.is(e,t))return!0;if(!e||!t)return!1;let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;let r=e,i=t;for(let e of n)if(!Object.is(r[e],i[e]))return!1;return!0}function q(e,t,n=!1,r=!0,i){let o=d(b(),e,t,i===void 0?{strict:n,ignoreQueryParams:r}:{strict:n,ignoreQueryParams:r,hash:i});return a(o.subscribe,o.getSnapshot,o.getSnapshot)}function J({children:e,fallback:t,onError:r}){let o=f(b()),s=a(o.subscribe,o.getSnapshot,o.getSnapshot),l=i(r);return l.current=r,n(()=>{s.error&&l.current?.(s.error,s.toRoute,s.fromRoute)},[s.version]),u(c,{children:[e,s.error?t(s.error,s.resetError):null]})}const Y=()=>{let e=t(y);if(!e)throw Error(`useNavigator must be used within a RouterProvider`);return e},X=()=>s(g(b()).getTree()),Z=()=>{let e=t(_);if(!e)throw Error(`useRoute must be used within a RouterProvider`);if(!e.route)throw Error(`useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?`);return e};function Q(){let e=h(b());return a(e.subscribe,e.getSnapshot,e.getSnapshot)}const $=({router:e,children:t,announceNavigation:i,scrollRestoration:s,viewTransitions:c})=>{n(()=>{if(!i)return;let t=T(e);return()=>{t.destroy()}},[i,e]);let u=s?.mode,d=s?.anchorScrolling,f=s!==void 0;n(()=>{if(!f)return;let t=M(e,{mode:u,anchorScrolling:d,scrollContainer:s.scrollContainer});return()=>{t.destroy()}},[e,f,u,d]),n(()=>{if(!c)return;let t=z(e);return()=>{t.destroy()}},[e,c]);let p=r(()=>o(e),[e]),h=r(()=>m(e),[e]),g=a(h.subscribe,h.getSnapshot,h.getSnapshot),b=r(()=>({navigator:p,route:g.route,previousRoute:g.previousRoute}),[p,g]);return l(v.Provider,{value:e,children:l(y.Provider,{value:p,children:l(_.Provider,{value:b,children:t})})})};export{Y as a,G as c,K as d,B as f,b as g,x as h,X as i,H as l,S as m,Q as n,J as o,C as p,Z as r,q as s,$ as t,U as u};
2
+ //# sourceMappingURL=RouterProvider-BCE2OtYs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterProvider-BCE2OtYs.mjs","names":["NOOP_INSTANCE"],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/view-transitions.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouterTransition.tsx","../../src/RouterProvider.tsx"],"sourcesContent":["// packages/react/src/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/src/hooks/useRouter.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = useContext(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const store = useMemo(\n () => createRouteNodeSource(router, nodeName),\n [router, nodeName],\n );\n\n // Use snapshot reference directly. createRouteNodeSource via stabilizeState\n // returns the SAME snapshot when the node-relevant state did not change,\n // so memoization on `[navigator, snapshot]` preserves identity for consumers.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n // getNavigator is WeakMap-cached in core; additional useMemo is redundant.\n const navigator = getNavigator(router);\n\n return useMemo(\n (): RouteContext => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n}\n","// packages/react/src/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","import type { Router, State } from \"@real-router/core\";\n\nconst CLEAR_DELAY = 7000;\nconst SAFARI_READY_DELAY = 100;\nconst ANNOUNCER_ATTR = \"data-real-router-announcer\";\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0\";\n\nexport interface RouteAnnouncerOptions {\n prefix?: string;\n getAnnouncementText?: (route: State) => string;\n}\n\nexport function createRouteAnnouncer(\n router: Router,\n options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n const prefix = options?.prefix ?? \"Navigated to \";\n const getCustomText = options?.getAnnouncementText;\n\n let isInitialNavigation = true;\n let isReady = false;\n let isDestroyed = false;\n let lastAnnouncedText = \"\";\n let pendingText: string | null = null;\n let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const doAnnounce = (text: string, h1: HTMLElement | null): void => {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus(h1);\n };\n\n // Safari-ready delay: announcing before VoiceOver wires up the aria-live region\n // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms\n // before marking the announcer \"ready\" — any navigation during that window is\n // buffered in pendingText and flushed once the delay expires.\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n\n if (pendingText !== null && !isDestroyed) {\n const text = pendingText;\n\n pendingText = null;\n doAnnounce(text, document.querySelector<HTMLElement>(\"h1\"));\n }\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n // Double rAF: waits for two paint frames so the incoming route's DOM\n // (including the new <h1>) is fully rendered before resolveText reads it.\n // Single rAF fires before the new route's template has been attached,\n // which would cause resolveText to pick up the OLD h1 or fall back to\n // document.title / route.name prematurely.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n const text = resolveText(route, prefix, getCustomText, h1);\n\n if (!text || text === lastAnnouncedText) {\n return;\n }\n\n if (!isReady) {\n // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n pendingText = text;\n\n return;\n }\n\n doAnnounce(text, h1);\n });\n });\n });\n\n return {\n destroy() {\n isDestroyed = true;\n unsubscribe();\n clearTimeout(clearTimeoutId);\n clearTimeout(safariTimeoutId);\n removeAnnouncer();\n },\n };\n}\n\nfunction getOrCreateAnnouncer(): HTMLElement {\n const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n if (existing) {\n return existing;\n }\n\n const element = document.createElement(\"div\");\n\n element.setAttribute(\"style\", VISUALLY_HIDDEN);\n element.setAttribute(\"aria-live\", \"assertive\");\n element.setAttribute(\"aria-atomic\", \"true\");\n element.setAttribute(ANNOUNCER_ATTR, \"\");\n\n document.body.prepend(element);\n\n return element;\n}\n\nfunction removeAnnouncer(): void {\n document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();\n}\n\nfunction resolveText(\n route: State,\n prefix: string,\n getCustomText: ((route: State) => string) | undefined,\n h1: HTMLElement | null,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1Text = (h1?.textContent ?? \"\").trim();\n const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n ? \"\"\n : route.name;\n const rawText =\n h1Text || document.title || routeName || globalThis.location.pathname;\n\n return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n if (!h1) {\n return;\n }\n\n if (!h1.hasAttribute(\"tabindex\")) {\n h1.setAttribute(\"tabindex\", \"-1\");\n }\n\n h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst STORAGE_KEY = \"real-router:scroll\";\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"manual\";\n\nexport interface ScrollRestorationOptions {\n mode?: ScrollRestorationMode | undefined;\n anchorScrolling?: boolean | undefined;\n scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\ninterface NavigationContext {\n direction?: \"forward\" | \"back\" | \"unknown\";\n navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n router: Router,\n options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n if (typeof globalThis.window === \"undefined\") {\n return NOOP_INSTANCE;\n }\n\n const mode = options?.mode ?? \"restore\";\n\n // mode \"manual\" = utility does nothing. Don't flip history.scrollRestoration,\n // don't subscribe, don't register pagehide — leave the browser's native\n // auto-restore intact for the app to override if it wants to.\n if (mode === \"manual\") {\n return NOOP_INSTANCE;\n }\n\n const anchorEnabled = options?.anchorScrolling ?? true;\n const getContainer = options?.scrollContainer;\n\n const prevScrollRestoration = history.scrollRestoration;\n\n try {\n history.scrollRestoration = \"manual\";\n } catch {\n // Ignore — some embedded contexts may reject the assignment.\n }\n\n // Resolve the container lazily on every event so containers mounted AFTER\n // the provider still get correct scroll handling. Falls back to window when\n // the getter is absent or returns null (pre-mount).\n const readPos = (): number => {\n const element = getContainer?.();\n\n return element ? element.scrollTop : globalThis.scrollY;\n };\n\n const writePos = (top: number): void => {\n const element = getContainer?.();\n\n if (element) {\n element.scrollTop = top;\n } else {\n globalThis.scrollTo(0, top);\n }\n };\n\n const scrollToHashOrTop = (route: State): void => {\n // URL plugin path (#532): `state.context.url.hash` is the source of truth\n // when one of the URL plugins (browser-plugin / navigation-plugin) is\n // installed. The value is already DECODED — feeding it through\n // `decodeURIComponent` again would throw on a bare `%`.\n const ctxHash = (route.context as { url?: { hash?: string } } | undefined)\n ?.url?.hash;\n\n if (ctxHash !== undefined) {\n if (anchorEnabled && ctxHash.length > 0) {\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(ctxHash);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n\n return;\n }\n\n // Fallback path: no URL plugin, read the DOM. `location.hash` is\n // percent-encoded; ids in the DOM are the raw string, so decode for the\n // match. Fall back to the raw slice if the hash contains a malformed\n // escape sequence (decodeURIComponent throws on those).\n const hash = globalThis.location.hash;\n\n if (anchorEnabled && hash.length > 1) {\n let id: string;\n\n try {\n id = decodeURIComponent(hash.slice(1));\n } catch {\n id = hash.slice(1);\n }\n\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(id);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n };\n\n let destroyed = false;\n\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n const nav = (route.context as { navigation?: NavigationContext })\n .navigation;\n\n // Browsers dispatch reload as the initial navigation after refresh, so\n // previousRoute is undefined and capture is naturally skipped. The\n // pre-refresh position was already persisted via pagehide.\n if (previousRoute) {\n putPos(keyOf(previousRoute), readPos());\n }\n\n // Single rAF so DOM is committed before we read anchors / write scroll.\n // Guard against destroy() racing with the callback.\n requestAnimationFrame(() => {\n if (destroyed) {\n return;\n }\n\n if (mode === \"top\" || !nav) {\n scrollToHashOrTop(route);\n\n return;\n }\n\n if (nav.navigationType === \"replace\") {\n return;\n }\n\n if (\n nav.direction === \"back\" ||\n nav.navigationType === \"traverse\" ||\n nav.navigationType === \"reload\"\n ) {\n writePos(loadStore()[keyOf(route)] ?? 0);\n\n return;\n }\n\n scrollToHashOrTop(route);\n });\n });\n\n const onPageHide = (): void => {\n const current = router.getState();\n\n if (current) {\n putPos(keyOf(current), readPos());\n }\n };\n\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n return {\n destroy: () => {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n unsubscribe();\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n try {\n history.scrollRestoration = prevScrollRestoration;\n } catch {\n // Ignore.\n }\n },\n };\n}\n\nfunction keyOf(state: State): string {\n return `${state.name}:${canonicalJson(state.params)}`;\n}\n\nfunction loadStore(): Record<string, number> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n\n return raw ? (JSON.parse(raw) as Record<string, number>) : {};\n } catch {\n return {};\n }\n}\n\nfunction putPos(key: string, pos: number): void {\n try {\n const store = loadStore();\n\n store[key] = pos;\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // Ignore quota / security errors.\n }\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, canonicalReplacer);\n}\n\nfunction canonicalReplacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable\n const keys = Object.keys(val as Record<string, unknown>).sort(\n (left: string, right: string) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { Router } from \"@real-router/core\";\n\nexport interface ViewTransitions {\n destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ViewTransitions = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport function createViewTransitions(router: Router): ViewTransitions {\n if (\n typeof document === \"undefined\" ||\n typeof document.startViewTransition !== \"function\"\n ) {\n return NOOP_INSTANCE;\n }\n\n let closeVT: (() => void) | null = null;\n let currentVT: { skipTransition?: () => void } | null = null;\n // Tracks whether TRANSITION_SUCCESS fired for the current leave. Used to\n // distinguish \"benign cleanup abort\" (router's async path aborts its own\n // controller in a finally block after successful navigation) from \"real\n // cancellation\" (concurrent navigate, guard rejection, dispose).\n let successFired = false;\n\n const resolveAndClear = (): void => {\n closeVT?.();\n closeVT = null;\n };\n\n const offLeave = router.subscribeLeave(({ signal }) => {\n // Reentrant abort: signal already aborted when we're called. Open no VT\n // — router will fall through to TRANSITION_CANCELLED via isCurrentNav()\n // after leave resolves. addEventListener(\"abort\", ...) does not re-fire\n // for past events, so skipping startViewTransition is the safe path.\n if (signal.aborted) {\n return;\n }\n\n successFired = false;\n resolveAndClear();\n\n // Return a Promise so the router awaits until the browser invokes\n // updateCallback. This ensures old DOM snapshot is captured BEFORE the\n // router commits the new state — giving correct exit→state→entry\n // ordering (vs fire-and-forget, where URL changes before VT captures).\n return new Promise<void>((resolveLeave) => {\n // Capture the resolver synchronously BEFORE startViewTransition() is\n // called. The browser invokes updateCallback in a later task, but\n // router.subscribe (TRANSITION_SUCCESS) can fire before that. If we\n // captured `resolve` inside the callback, subscribe would see closeVT\n // still null and skip resolving — the deferred would hang for 4s\n // until the VT API aborts with TimeoutError.\n const deferred = new Promise<void>((resolve) => {\n closeVT = resolve;\n });\n\n signal.addEventListener(\n \"abort\",\n () => {\n if (successFired) {\n // Router's async path (#finishAsyncNavigation) aborts its own\n // controller in a finally block AFTER completeTransition (and\n // thus AFTER subscribe fired). This is cleanup, not\n // cancellation — VT is progressing normally, do nothing.\n return;\n }\n\n // Real cancellation (concurrent navigate, dispose). Resolve the\n // deferred so updateCallback can complete, skip the VT so no\n // stale animation leaks, and unblock the router if the abort\n // fires before updateCallback was invoked.\n resolveAndClear();\n currentVT?.skipTransition?.();\n resolveLeave();\n },\n { once: true },\n );\n\n try {\n currentVT = document.startViewTransition(() => {\n // Resolving here unblocks the router at the moment the browser\n // enters updateCallback — by spec, old DOM snapshot is captured\n // before this callback runs. Router now proceeds through\n // activation guards and setState; the VT animation waits on\n // `deferred`, which is resolved from router.subscribe after a\n // task-queue tick (see NOTE on setTimeout below).\n resolveLeave();\n\n return deferred;\n });\n } catch {\n // Defensive: spec says startViewTransition doesn't throw under\n // normal conditions, but Chromium has had edge cases (detached\n // document, extension interference). Clean up and unblock router.\n resolveAndClear();\n resolveLeave();\n }\n });\n });\n\n const offSuccess = router.subscribe(() => {\n const resolver = closeVT;\n\n successFired = true;\n closeVT = null;\n\n if (resolver === null) {\n currentVT = null;\n } else {\n // CRITICAL: CANNOT use requestAnimationFrame here. When the router\n // takes the async path (leave returned a Promise), subscribe fires\n // AFTER the browser has already transitioned VT into the\n // \"update-callback-called\" phase. In that phase Chromium sets\n // rendering suppression to true, which ALSO blocks rAF callbacks.\n // rAF would never fire → deferred never resolves → browser aborts\n // vt.ready with TimeoutError after 4s (observed in Chromium).\n //\n // setTimeout runs on the task queue independent of the rendering\n // pipeline, so it fires regardless of suppression. React's scheduler\n // uses MessageChannel tasks, which are queued before our setTimeout,\n // so the new DOM is committed by the time our callback runs.\n setTimeout(() => {\n resolver();\n currentVT = null;\n }, 0);\n }\n });\n\n return {\n destroy: () => {\n offLeave();\n offSuccess();\n currentVT?.skipTransition?.();\n currentVT = null;\n resolveAndClear();\n },\n };\n}\n","import type {\n NavigationOptions,\n Params,\n Router,\n State,\n} from \"@real-router/core\";\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 &&\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\n/**\n * RFC 3986 fragment encoding: preserve sub-delims (`&`, `=`, `?`, `:`),\n * encode space, `%`, control chars, non-ASCII via encodeURI; defensively\n * escape `#` (encodeURI does not). Mirrors `encodeHashFragment` in\n * `shared/browser-env/url-context.ts` — duplicated here because the\n * shared/dom-utils symlink graph does not reach shared/browser-env.\n */\nfunction encodeFragmentInline(decoded: string): string {\n return encodeURI(decoded).replaceAll(\"#\", \"%23\");\n}\n\ntype BuildUrlFn = (\n name: string,\n params: Params,\n options?: { hash?: string },\n) => string | undefined;\n\n/**\n * Builds an href for a `<Link>` element.\n *\n * - Prefers the URL plugin's `buildUrl` (browser-plugin, navigation-plugin,\n * hash-plugin) when present.\n * - Falls back to `router.buildPath` for runtimes without a URL plugin\n * (memory-plugin, console UIs, NativeScript). In that fallback the hash\n * is appended manually so the rendered href is still correct.\n * - The optional 4th argument is an options object so the contract stays\n * extensible. The `hash` option is a decoded fragment without leading \"#\";\n * `<Link hash=\"#section\">` is accepted defensively (leading \"#\" stripped).\n * Frozen API: previous 3-arg call sites continue to work unchanged.\n */\nexport function buildHref(\n router: Router,\n routeName: string,\n routeParams: Params,\n options?: { hash?: string },\n): string | undefined {\n try {\n const rawHash = options?.hash;\n let normHash: string | undefined;\n\n if (rawHash !== undefined) {\n normHash = rawHash.startsWith(\"#\") ? rawHash.slice(1) : rawHash;\n }\n\n const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n if (buildUrl) {\n const url = buildUrl(\n routeName,\n routeParams,\n normHash === undefined ? undefined : { hash: normHash },\n );\n\n if (url !== undefined) {\n return url;\n }\n }\n\n const path = router.buildPath(routeName, routeParams);\n\n return normHash ? `${path}#${encodeFragmentInline(normHash)}` : path;\n } catch {\n console.error(\n `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n );\n\n return undefined;\n }\n}\n\n/**\n * `<Link>` click-handler navigation helper (#532).\n *\n * Wraps `router.navigate(name, params, opts)` with same-route different-hash\n * detection: when the consumer clicks a hash-bearing Link that targets the\n * current route with the same params but a different fragment, core's\n * SAME_STATES check would otherwise reject the navigation. The helper adds\n * `force: true` and `hashChange: true` automatically — subscribers can then\n * disambiguate via `state.context.url.hashChanged`.\n *\n * For pure programmatic same-route hash-only navigation, callers are\n * documented to pass `{ force: true }` themselves; the auto-bypass here is\n * a UX convenience for `<Link hash>` that all 6 framework adapters share.\n */\n/**\n * Local extended-options type. Adapters that depend only on `@real-router/core`\n * (without a URL plugin) do not see the `NavigationOptions` augmentation that\n * declares `hash` / `hashChange`. Casting to this widened type inside the\n * helper keeps shared/dom-utils self-contained — adapters do not need to\n * augment NavigationOptions themselves to consume `<Link hash>`.\n */\ntype HashAwareNavigationOptions = NavigationOptions & {\n hash?: string;\n hashChange?: boolean;\n};\n\nexport function navigateWithHash(\n router: Router,\n routeName: string,\n routeParams: Params,\n hash: string | undefined,\n extraOptions?: NavigationOptions,\n): Promise<State> {\n const opts: HashAwareNavigationOptions = { ...extraOptions };\n\n if (hash !== undefined) {\n opts.hash = hash;\n }\n\n const current = router.getState();\n\n if (\n current?.name === routeName &&\n shallowEqual(current.params, routeParams)\n ) {\n const currentHash =\n (current.context as { url?: { hash?: string } } | undefined)?.url?.hash ??\n \"\";\n const newHash = hash ?? currentHash;\n\n if (currentHash !== newHash) {\n opts.force = true;\n opts.hashChange = true;\n }\n }\n\n return router.navigate(routeName, routeParams, opts);\n}\n\nfunction parseTokens(value: string | undefined): string[] {\n return value ? (value.match(/\\S+/g) ?? []) : [];\n}\n\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n const activeTokens = parseTokens(activeClassName);\n\n if (activeTokens.length === 0) {\n return baseClassName ?? undefined;\n }\n if (!baseClassName) {\n return activeTokens.join(\" \");\n }\n\n const baseTokens = parseTokens(baseClassName);\n const seen = new Set(baseTokens);\n\n for (const token of activeTokens) {\n if (!seen.has(token)) {\n seen.add(token);\n baseTokens.push(token);\n }\n }\n\n return baseTokens.join(\" \");\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function shallowEqual(\n prev: object | undefined,\n next: object | undefined,\n): boolean {\n if (Object.is(prev, next)) {\n return true;\n }\n if (!prev || !next) {\n return false;\n }\n\n const prevKeys = Object.keys(prev);\n\n if (prevKeys.length !== Object.keys(next).length) {\n return false;\n }\n\n const prevRecord = prev as Record<string, unknown>;\n const nextRecord = next as Record<string, unknown>;\n\n for (const key of prevKeys) {\n if (!Object.is(prevRecord[key], nextRecord[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function applyLinkA11y(element: HTMLElement | null | undefined): void {\n if (!element) {\n return;\n }\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.hasAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Params } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n routeName: string,\n params?: Params,\n strict = false,\n ignoreQueryParams = true,\n hash?: string,\n): boolean {\n const router = useRouter();\n\n // createActiveRouteSource is per-router + canonical-args cached in\n // @real-router/sources, so passing params by reference is safe — equivalent\n // param shapes hit the same cache entry regardless of key order. The\n // `hash` argument (#532) is part of the cache key when defined: a Link\n // pointing to `/settings#account` shares its source only with other\n // consumers using the same routeName + params + hash.\n // exactOptionalPropertyTypes forbids `{ hash: undefined }` literally, so\n // we conditionally include the key only when the caller passed a value.\n const store = createActiveRouteSource(\n router,\n routeName,\n params,\n hash === undefined\n ? { strict, ignoreQueryParams }\n : { strict, ignoreQueryParams, hash },\n );\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { createDismissableError } from \"@real-router/sources\";\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { ReactNode, JSX } from \"react\";\n\nexport interface RouterErrorBoundaryProps {\n readonly children: ReactNode;\n readonly fallback: (error: RouterError, resetError: () => void) => ReactNode;\n readonly onError?: (\n error: RouterError,\n toRoute: State | null,\n fromRoute: State | null,\n ) => void;\n}\n\nexport function RouterErrorBoundary({\n children,\n fallback,\n onError,\n}: RouterErrorBoundaryProps): JSX.Element {\n const router = useRouter();\n const store = createDismissableError(router);\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n\n const onErrorRef = useRef(onError);\n\n // eslint-disable-next-line @eslint-react/refs -- \"latest ref\" pattern: sync callback to ref to avoid effect re-runs\n onErrorRef.current = onError;\n\n useEffect(() => {\n if (snapshot.error) {\n onErrorRef.current?.(\n snapshot.error,\n snapshot.toRoute,\n snapshot.fromRoute,\n );\n }\n // eslint-disable-next-line @eslint-react/exhaustive-deps -- onError tracked via ref, snapshot fields accessed inside callback\n }, [snapshot.version]);\n\n return (\n <>\n {children}\n {snapshot.error ? fallback(snapshot.error, snapshot.resetError) : null}\n </>\n );\n}\n","// packages/react/src/hooks/useNavigator.tsx\n\nimport { useContext } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = useContext(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getPluginApi } from \"@real-router/core/api\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside\n * `@real-router/route-utils`, so the same router always returns the same\n * `RouteUtils` instance across renders — no local cache needed in the adapter.\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n","// packages/react/src/hooks/useRoute.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\nimport type { Params, State } from \"@real-router/core\";\n\nexport const useRoute = <P extends Params = Params>(): Omit<\n RouteContextType<P>,\n \"route\"\n> & { route: State<P> } => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouterProvider\");\n }\n\n if (!routeContext.route) {\n throw new Error(\n \"useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?\",\n );\n }\n\n return routeContext as Omit<RouteContextType<P>, \"route\"> & {\n route: State<P>;\n };\n};\n","import { getTransitionSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\n\nexport function useRouterTransition(): RouterTransitionSnapshot {\n const router = useRouter();\n const store = getTransitionSource(router);\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { useEffect, useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\nimport {\n createRouteAnnouncer,\n createScrollRestoration,\n createViewTransitions,\n} from \"./dom-utils\";\n\nimport type { ScrollRestorationOptions } from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n announceNavigation?: boolean;\n scrollRestoration?: ScrollRestorationOptions;\n viewTransitions?: boolean;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\n scrollRestoration,\n viewTransitions,\n}) => {\n useEffect(() => {\n if (!announceNavigation) {\n return;\n }\n\n const announcer = createRouteAnnouncer(router);\n\n return () => {\n announcer.destroy();\n };\n }, [announceNavigation, router]);\n\n // Primitive deps so inline `{ mode: \"restore\" }` doesn't thrash on every\n // render. scrollContainer is a getter invoked lazily on every event inside\n // the utility — swapping its reference doesn't change the resolved element,\n // so we intentionally omit it from deps to keep inline getters stable.\n const srMode = scrollRestoration?.mode;\n const srAnchor = scrollRestoration?.anchorScrolling;\n const srEnabled = scrollRestoration !== undefined;\n\n useEffect(() => {\n if (!srEnabled) {\n return;\n }\n\n const sr = createScrollRestoration(router, {\n mode: srMode,\n anchorScrolling: srAnchor,\n // srEnabled check above guarantees scrollRestoration is defined.\n scrollContainer: scrollRestoration.scrollContainer,\n });\n\n return () => {\n sr.destroy();\n };\n // scrollRestoration (for scrollContainer) omitted — see comment above.\n // eslint-disable-next-line @eslint-react/exhaustive-deps\n }, [router, srEnabled, srMode, srAnchor]);\n\n useEffect(() => {\n if (!viewTransitions) {\n return;\n }\n\n const vt = createViewTransitions(router);\n\n return () => {\n vt.destroy();\n };\n }, [router, viewTransitions]);\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // useSyncExternalStore manages the router subscription lifecycle:\n // subscribe connects to router on first listener, unsubscribes on last.\n // This is Strict Mode safe — no useEffect cleanup needed.\n const store = useMemo(() => createRouteSource(router), [router]);\n // Use snapshot reference directly. createRouteSource via stabilizeState\n // returns the SAME snapshot reference when route.path is unchanged, so\n // useMemo below sees stable deps for idempotent navigations and\n // RouteContext consumers do not re-render.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const routeContextValue = useMemo(\n () => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n\n return (\n <RouterContext.Provider value={router}>\n <NavigatorContext.Provider value={navigator}>\n <RouteContext.Provider value={routeContextValue}>\n {children}\n </RouteContext.Provider>\n </NavigatorContext.Provider>\n </RouterContext.Provider>\n );\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { l as LinkProps } from "./useRouterTransition-B3UWzXYl.mjs";
1
+ import { l as LinkProps } from "./useRouterTransition-B65Wvngj.mjs";
2
2
  import { FC, ReactNode } from "react";
3
- import { Params, Router, State } from "@real-router/core";
3
+ import { NavigationOptions, Params, Router, State } from "@real-router/core";
4
4
 
5
5
  //#region src/components/Link.d.ts
6
6
  declare const Link: FC<LinkProps>;
@@ -24,4 +24,4 @@ interface RouteProviderProps {
24
24
  declare const RouterProvider: FC<RouteProviderProps>;
25
25
  //#endregion
26
26
  export { Link as n, RouterProvider as t };
27
- //# sourceMappingURL=RouterProvider-D2wFs7Sd.d.mts.map
27
+ //# sourceMappingURL=RouterProvider-CtpRoTCZ.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterProvider-D2wFs7Sd.d.mts","names":[],"sources":["../../src/components/Link.tsx","../../../../shared/dom-utils/scroll-restore.ts","../../src/RouterProvider.tsx"],"mappings":";;;;;cAmGa,IAAA,EAAM,EAAA,CAAG,SAAA;;;KCzFV,qBAAA;AAAA,UAEK,wBAAA;EACf,IAAA,GAAO,qBAAA;EACP,eAAA;EACA,eAAA,UAAyB,WAAA;AAAA;;;UCAV,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;EACA,iBAAA,GAAoB,wBAAA;EACpB,eAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
1
+ {"version":3,"file":"RouterProvider-CtpRoTCZ.d.mts","names":[],"sources":["../../src/components/Link.tsx","../../../../shared/dom-utils/scroll-restore.ts","../../src/RouterProvider.tsx"],"mappings":";;;;;cAsHa,IAAA,EAAM,EAAA,CAAG,SAAA;;;KC5GV,qBAAA;AAAA,UAEK,wBAAA;EACf,IAAA,GAAO,qBAAA;EACP,eAAA;EACA,eAAA,UAAyB,WAAA;AAAA;;;UCAV,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;EACA,iBAAA,GAAoB,wBAAA;EACpB,eAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
@@ -1,5 +1,5 @@
1
- import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-B3UWzXYl.mjs";
2
- import { n as Link, t as RouterProvider } from "./RouterProvider-D2wFs7Sd.mjs";
1
+ import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-B65Wvngj.mjs";
2
+ import { n as Link, t as RouterProvider } from "./RouterProvider-CtpRoTCZ.mjs";
3
3
  import { ReactElement, ReactNode } from "react";
4
4
  import { Navigator, State } from "@real-router/core";
5
5
  import { RouterTransitionSnapshot } from "@real-router/sources";
@@ -1,2 +1,2 @@
1
- import{a as e,h as t,i as n,m as r,n as i,o as a,r as o,t as s}from"./RouterProvider-D84Tsw-s.mjs";import{t as c}from"./Link-W2dQSXXI.mjs";import{Activity as l,Children as u,Fragment as d,Suspense as f,isValidElement as p,useEffect as m,useLayoutEffect as h,useMemo as g,useRef as _}from"react";import{UNKNOWN_ROUTE as v}from"@real-router/core";import{startsWithSegment as y}from"@real-router/route-utils";import{Fragment as b,jsx as x}from"react/jsx-runtime";function S(e){return null}S.displayName=`RouteView.Match`;function C(e){return null}C.displayName=`RouteView.Self`;function w(e){return null}w.displayName=`RouteView.NotFound`;function T(e,t,n){return t===``?!1:n?e===t:y(e,t)}function E(e,t){for(let n of u.toArray(e))p(n)&&(n.type===S||n.type===C||n.type===w?t.push(n):E(n.props.children,t))}function D(e,t,n,r,i){let a=i===void 0?e:x(f,{fallback:i,children:e});return n?x(l,{mode:r,children:a},t):x(d,{children:a},t)}function O(e,t){return e.type===w?(t.notFoundChildren=e.props.children,!0):e.type===C?(t.selfFound||=(t.selfChildren=e.props.children,t.selfFallback=e.props.fallback,!0),!0):!1}function k(e,t,n,r,i){let{segment:a,exact:o=!1,keepAlive:s=!1,fallback:c}=e.props,l=n?`${n}.${a}`:a;return!i&&T(t,l,o)?(r.add(l),{rendered:D(e.props.children,l,s,`visible`,c),matched:!0}):s&&r.has(l)?{rendered:D(e.props.children,l,s,`hidden`,c),matched:!1}:{rendered:null,matched:!1}}function A(e,t,n,r){if(r.selfFound&&t===n){e.push(D(r.selfChildren,`__route-view-self__`,!1,`visible`,r.selfFallback));return}t===v&&r.notFoundChildren!==null&&e.push(x(d,{children:r.notFoundChildren},`__route-view-not-found__`))}function j(e,t,n,r){let i={selfChildren:null,selfFallback:void 0,selfFound:!1,notFoundChildren:null},a=!1,o=[];for(let s of e){if(O(s,i))continue;let e=k(s,t,n,r,a);e.matched&&(a=!0),e.rendered!==null&&o.push(e.rendered)}return a||A(o,t,n,i),{rendered:o,activeMatchFound:a}}function M({nodeName:e,children:t}){let{route:n}=r(e),i=_(null);i.current??=new Set;let a=g(()=>{let e=[];return E(t,e),e},[t]);if(!n)return null;let{rendered:o}=j(a,n.name,e,i.current);return o.length>0?x(b,{children:o}):null}M.displayName=`RouteView`;const N=Object.assign(M,{Match:S,Self:C,NotFound:w});function P(e,n){let r=t(),i=_(e),a=n?.skipSameRoute??!0;h(()=>{i.current=e}),m(()=>r.subscribeLeave(({route:e,nextRoute:t,signal:n})=>{if(!(a&&e.name===t.name)&&!n.aborted)return i.current({route:e,nextRoute:t,signal:n})}),[r,a])}function F(e,t){let{route:n,previousRoute:r}=o(),i=_(e),a=_(null),s=t?.skipSameRoute??!0;h(()=>{i.current=e}),m(()=>{n.transition.from&&(s&&n.transition.from===n.name||a.current===n||!r||(a.current=n,i.current({route:n,previousRoute:r})))},[n,r,s])}export{c as Link,N as RouteView,a as RouterErrorBoundary,s as RouterProvider,e as useNavigator,o as useRoute,F as useRouteEnter,P as useRouteExit,r as useRouteNode,n as useRouteUtils,t as useRouter,i as useRouterTransition};
1
+ import{a as e,g as t,h as n,i as r,n as i,o as a,r as o,t as s}from"./RouterProvider-BCE2OtYs.mjs";import{t as c}from"./Link-B_gDxNSU.mjs";import{Activity as l,Children as u,Fragment as d,Suspense as f,isValidElement as p,useEffect as m,useLayoutEffect as h,useMemo as g,useRef as _}from"react";import{UNKNOWN_ROUTE as v}from"@real-router/core";import{startsWithSegment as y}from"@real-router/route-utils";import{Fragment as b,jsx as x}from"react/jsx-runtime";function S(e){return null}S.displayName=`RouteView.Match`;function C(e){return null}C.displayName=`RouteView.Self`;function w(e){return null}w.displayName=`RouteView.NotFound`;function T(e,t,n){return t===``?!1:n?e===t:y(e,t)}function E(e,t){for(let n of u.toArray(e))p(n)&&(n.type===S||n.type===C||n.type===w?t.push(n):E(n.props.children,t))}function D(e,t,n,r,i){let a=i===void 0?e:x(f,{fallback:i,children:e});return n?x(l,{mode:r,children:a},t):x(d,{children:a},t)}function O(e,t){return e.type===w?(t.notFoundChildren=e.props.children,!0):e.type===C?(t.selfFound||=(t.selfChildren=e.props.children,t.selfFallback=e.props.fallback,!0),!0):!1}function k(e,t,n,r,i){let{segment:a,exact:o=!1,keepAlive:s=!1,fallback:c}=e.props,l=n?`${n}.${a}`:a;return!i&&T(t,l,o)?(r.add(l),{rendered:D(e.props.children,l,s,`visible`,c),matched:!0}):s&&r.has(l)?{rendered:D(e.props.children,l,s,`hidden`,c),matched:!1}:{rendered:null,matched:!1}}function A(e,t,n,r){if(r.selfFound&&t===n){e.push(D(r.selfChildren,`__route-view-self__`,!1,`visible`,r.selfFallback));return}t===v&&r.notFoundChildren!==null&&e.push(x(d,{children:r.notFoundChildren},`__route-view-not-found__`))}function j(e,t,n,r){let i={selfChildren:null,selfFallback:void 0,selfFound:!1,notFoundChildren:null},a=!1,o=[];for(let s of e){if(O(s,i))continue;let e=k(s,t,n,r,a);e.matched&&(a=!0),e.rendered!==null&&o.push(e.rendered)}return a||A(o,t,n,i),{rendered:o,activeMatchFound:a}}function M({nodeName:e,children:t}){let{route:r}=n(e),i=_(null);i.current??=new Set;let a=g(()=>{let e=[];return E(t,e),e},[t]);if(!r)return null;let{rendered:o}=j(a,r.name,e,i.current);return o.length>0?x(b,{children:o}):null}M.displayName=`RouteView`;const N=Object.assign(M,{Match:S,Self:C,NotFound:w});function P(e,n){let r=t(),i=_(e),a=n?.skipSameRoute??!0;h(()=>{i.current=e}),m(()=>r.subscribeLeave(({route:e,nextRoute:t,signal:n})=>{if(!(a&&e.name===t.name)&&!n.aborted)return i.current({route:e,nextRoute:t,signal:n})}),[r,a])}function F(e,t){let{route:n,previousRoute:r}=o(),i=_(e),a=_(null),s=t?.skipSameRoute??!0;h(()=>{i.current=e}),m(()=>{n.transition.from&&(s&&n.transition.from===n.name||a.current===n||!r||(a.current=n,i.current({route:n,previousRoute:r})))},[n,r,s])}export{c as Link,N as RouteView,a as RouterErrorBoundary,s as RouterProvider,e as useNavigator,o as useRoute,F as useRouteEnter,P as useRouteExit,n as useRouteNode,r as useRouteUtils,t as useRouter,i as useRouterTransition};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1,4 +1,4 @@
1
- import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-B3UWzXYl.mjs";
1
+ import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-B65Wvngj.mjs";
2
2
  import { FC, ReactNode } from "react";
3
3
  import { NavigationOptions, Navigator, Params, Router } from "@real-router/core";
4
4
  import { RouterTransitionSnapshot } from "@real-router/sources";
package/dist/esm/ink.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{a as e,f as t,h as n,i as r,m as i,n as a,o,p as s,r as c,s as l,t as u,u as d}from"./RouterProvider-D84Tsw-s.mjs";import{memo as f,useCallback as p}from"react";import{jsx as m}from"react/jsx-runtime";import{Text as h,useFocus as g,useInput as _}from"ink";function v(e,t){return e.routeName===t.routeName&&e.activeStrict===t.activeStrict&&e.ignoreQueryParams===t.ignoreQueryParams&&e.color===t.color&&e.activeColor===t.activeColor&&e.focusColor===t.focusColor&&e.inverse===t.inverse&&e.activeInverse===t.activeInverse&&e.focusInverse===t.focusInverse&&e.id===t.id&&e.autoFocus===t.autoFocus&&e.onSelect===t.onSelect&&e.children===t.children&&d(e.routeParams,t.routeParams)&&d(e.routeOptions,t.routeOptions)}const y=f(({routeName:e,routeParams:r=s,routeOptions:i=t,activeStrict:a=!1,ignoreQueryParams:o=!0,color:c,activeColor:u,focusColor:d,inverse:f,activeInverse:v,focusInverse:y,id:b,autoFocus:x,onSelect:S,children:C})=>{let w=n(),{isFocused:T}=g({...b!==void 0&&{id:b},...x!==void 0&&{autoFocus:x}}),E=l(e,r,a,o),D=p(()=>{S?.(),w.navigate(e,r,i).catch(()=>{})},[S,w,e,r,i]);_((e,t)=>{t.return&&D()},{isActive:T});let O=c;T?O=d??u??c:E&&(O=u??c);let k=f;T?k=y??v??f:E&&(k=v??f);let A={};return O!==void 0&&(A.color=O),k!==void 0&&(A.inverse=k),m(h,{...A,children:C})},v);y.displayName=`InkLink`;const b=({router:e,children:t})=>m(u,{router:e,children:t});export{y as InkLink,b as InkRouterProvider,o as RouterErrorBoundary,e as useNavigator,c as useRoute,i as useRouteNode,r as useRouteUtils,n as useRouter,a as useRouterTransition};
1
+ import{a as e,d as t,g as n,h as r,i,m as a,n as o,o as s,p as c,r as l,s as u,t as d}from"./RouterProvider-BCE2OtYs.mjs";import{memo as f,useCallback as p}from"react";import{jsx as m}from"react/jsx-runtime";import{Text as h,useFocus as g,useInput as _}from"ink";function v(e,n){return e.routeName===n.routeName&&e.activeStrict===n.activeStrict&&e.ignoreQueryParams===n.ignoreQueryParams&&e.color===n.color&&e.activeColor===n.activeColor&&e.focusColor===n.focusColor&&e.inverse===n.inverse&&e.activeInverse===n.activeInverse&&e.focusInverse===n.focusInverse&&e.id===n.id&&e.autoFocus===n.autoFocus&&e.onSelect===n.onSelect&&e.children===n.children&&t(e.routeParams,n.routeParams)&&t(e.routeOptions,n.routeOptions)}const y=f(({routeName:e,routeParams:t=a,routeOptions:r=c,activeStrict:i=!1,ignoreQueryParams:o=!0,color:s,activeColor:l,focusColor:d,inverse:f,activeInverse:v,focusInverse:y,id:b,autoFocus:x,onSelect:S,children:C})=>{let w=n(),{isFocused:T}=g({...b!==void 0&&{id:b},...x!==void 0&&{autoFocus:x}}),E=u(e,t,i,o),D=p(()=>{S?.(),w.navigate(e,t,r).catch(()=>{})},[S,w,e,t,r]);_((e,t)=>{t.return&&D()},{isActive:T});let O=s;T?O=d??l??s:E&&(O=l??s);let k=f;T?k=y??v??f:E&&(k=v??f);let A={};return O!==void 0&&(A.color=O),k!==void 0&&(A.inverse=k),m(h,{...A,children:C})},v);y.displayName=`InkLink`;const b=({router:e,children:t})=>m(d,{router:e,children:t});export{y as InkLink,b as InkRouterProvider,s as RouterErrorBoundary,e as useNavigator,l as useRoute,r as useRouteNode,i as useRouteUtils,n as useRouter,o as useRouterTransition};
2
2
  //# sourceMappingURL=ink.mjs.map
@@ -1,5 +1,5 @@
1
- import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-B3UWzXYl.mjs";
2
- import { n as Link, t as RouterProvider } from "./RouterProvider-D2wFs7Sd.mjs";
1
+ import { a as useNavigator, c as RouterErrorBoundaryProps, i as useRouteUtils, l as LinkProps, n as useRouteNode, o as useRouter, r as useRoute, s as RouterErrorBoundary, t as useRouterTransition } from "./useRouterTransition-B65Wvngj.mjs";
2
+ import { n as Link, t as RouterProvider } from "./RouterProvider-CtpRoTCZ.mjs";
3
3
  import { Navigator } from "@real-router/core";
4
4
  import { RouterTransitionSnapshot } from "@real-router/sources";
5
5
  export { Link, type LinkProps, type Navigator, RouterErrorBoundary, type RouterErrorBoundaryProps, RouterProvider, type RouterTransitionSnapshot, useNavigator, useRoute, useRouteNode, useRouteUtils, useRouter, useRouterTransition };
@@ -1 +1 @@
1
- import{a as e,h as t,i as n,m as r,n as i,o as a,r as o,t as s}from"./RouterProvider-D84Tsw-s.mjs";import{t as c}from"./Link-W2dQSXXI.mjs";export{c as Link,a as RouterErrorBoundary,s as RouterProvider,e as useNavigator,o as useRoute,r as useRouteNode,n as useRouteUtils,t as useRouter,i as useRouterTransition};
1
+ import{a as e,g as t,h as n,i as r,n as i,o as a,r as o,t as s}from"./RouterProvider-BCE2OtYs.mjs";import{t as c}from"./Link-B_gDxNSU.mjs";export{c as Link,a as RouterErrorBoundary,s as RouterProvider,e as useNavigator,o as useRoute,n as useRouteNode,r as useRouteUtils,t as useRouter,i as useRouterTransition};
@@ -18,6 +18,16 @@ interface LinkProps<P extends Params = Params> extends HTMLAttributes<HTMLAnchor
18
18
  activeClassName?: string;
19
19
  activeStrict?: boolean;
20
20
  ignoreQueryParams?: boolean;
21
+ /**
22
+ * URL fragment (decoded form, no leading "#") (#532).
23
+ * - omitted/`undefined` → preserve current fragment on same-route navigation
24
+ * - `""` → clear fragment
25
+ * - non-empty → set fragment
26
+ *
27
+ * Requires a URL plugin (browser-plugin or navigation-plugin) for full
28
+ * round-trip; hash-plugin ignores the prop with a one-time dev warning.
29
+ */
30
+ hash?: string;
21
31
  target?: string;
22
32
  onClick?: MouseEventHandler<HTMLAnchorElement>;
23
33
  onMouseOver?: MouseEventHandler<HTMLAnchorElement>;
@@ -79,4 +89,4 @@ declare function useRouteNode(nodeName: string): RouteContext;
79
89
  declare function useRouterTransition(): RouterTransitionSnapshot;
80
90
  //#endregion
81
91
  export { useNavigator as a, RouterErrorBoundaryProps as c, useRouteUtils as i, LinkProps as l, useRouteNode as n, useRouter as o, useRoute as r, RouterErrorBoundary as s, useRouterTransition as t };
82
- //# sourceMappingURL=useRouterTransition-B3UWzXYl.d.mts.map
92
+ //# sourceMappingURL=useRouterTransition-B65Wvngj.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRouterTransition-B65Wvngj.d.mts","names":[],"sources":["../../src/types.ts","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useRouter.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useRouterTransition.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA,WAAuB,MAAA,GAAS,MAAA;EAC1C,SAAA,EAAW,SAAA;AAAA,IACT,UAAA,CAAW,CAAA;AAAA,UAEE,SAAA,WACL,MAAA,GAAS,MAAA,UACX,cAAA,CAAe,iBAAA;EACvB,SAAA;EACA,WAAA,GAAc,CAAA;EACd,YAAA,GAAe,iBAAA;EACf,eAAA;EACA,YAAA;EACA,iBAAA;EAjB0B;;;;;;;;;EA2B1B,IAAA;EACA,MAAA;EACA,OAAA,GAAU,iBAAA,CAAkB,iBAAA;EAC5B,WAAA,GAAc,iBAAA,CAAkB,iBAAA;AAAA;;;UC9BjB,wBAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,KAAA,EAAO,WAAA,EAAa,UAAA,iBAA2B,SAAA;EAAA,SAC1D,OAAA,IACP,KAAA,EAAO,WAAA,EACP,OAAA,EAAS,KAAA,SACT,SAAA,EAAW,KAAA;AAAA;AAAA,iBAIC,mBAAA,CAAA;EACd,QAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,GAAA,CAAI,OAAA;;;cCdrB,SAAA,QAAgB,MAAA;;;cCAhB,YAAA,QAAmB,SAAA;;;;;;;;AHAhC;;;;;;;;;;;;;;;;;;cIsBa,aAAA,QAAoB,UAAA;;;cCrBpB,QAAA,aAAsB,MAAA,GAAS,MAAA,OAAW,IAAA,CACrD,YAAA,CAAiB,CAAA;EAEb,KAAA,EAAO,KAAA,CAAM,CAAA;AAAA;;;iBCJH,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/react",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "type": "commonjs",
5
5
  "description": "React integration for Real-Router",
6
6
  "main": "./dist/cjs/index.js",
@@ -76,9 +76,9 @@
76
76
  "license": "MIT",
77
77
  "sideEffects": false,
78
78
  "dependencies": {
79
- "@real-router/core": "^0.50.2",
80
- "@real-router/route-utils": "^0.2.1",
81
- "@real-router/sources": "^0.7.2"
79
+ "@real-router/core": "^0.51.0",
80
+ "@real-router/route-utils": "^0.2.2",
81
+ "@real-router/sources": "^0.8.0"
82
82
  },
83
83
  "devDependencies": {
84
84
  "@testing-library/dom": "10.4.1",
@@ -88,7 +88,7 @@
88
88
  "ink": "7.0.1",
89
89
  "ink-testing-library": "4.0.0",
90
90
  "vitest-react-profiler": "1.12.0",
91
- "@real-router/browser-plugin": "^0.16.0"
91
+ "@real-router/browser-plugin": "^0.17.0"
92
92
  },
93
93
  "peerDependencies": {
94
94
  "@types/react": ">=18.0.0",
@@ -5,6 +5,7 @@ import {
5
5
  shouldNavigate,
6
6
  buildHref,
7
7
  buildActiveClassName,
8
+ navigateWithHash,
8
9
  shallowEqual,
9
10
  } from "../dom-utils";
10
11
  import { useIsActiveRoute } from "../hooks/useIsActiveRoute";
@@ -27,6 +28,7 @@ function areLinkPropsEqual(
27
28
  prev.target === next.target &&
28
29
  prev.style === next.style &&
29
30
  prev.children === next.children &&
31
+ prev.hash === next.hash &&
30
32
  shallowEqual(prev.routeParams, next.routeParams) &&
31
33
  shallowEqual(prev.routeOptions, next.routeOptions)
32
34
  );
@@ -40,6 +42,7 @@ const LinkImpl: FC<LinkProps> = ({
40
42
  activeClassName = "active",
41
43
  activeStrict = false,
42
44
  ignoreQueryParams = true,
45
+ hash,
43
46
  onClick,
44
47
  target,
45
48
  children,
@@ -52,16 +55,26 @@ const LinkImpl: FC<LinkProps> = ({
52
55
 
53
56
  const router = useRouter();
54
57
 
58
+ // When `hash` prop is set, active state requires both route AND hash to
59
+ // match (#532). Without this, three tab links sharing routeName="settings"
60
+ // would all be marked active by route-name alone, defeating tab semantics.
55
61
  const isActive = useIsActiveRoute(
56
62
  routeName,
57
63
  routeParams,
58
64
  activeStrict,
59
65
  ignoreQueryParams,
66
+ hash,
60
67
  );
61
68
 
62
69
  const href = useMemo(
63
- () => buildHref(router, routeName, routeParams),
64
- [router, routeName, routeParams],
70
+ () =>
71
+ buildHref(
72
+ router,
73
+ routeName,
74
+ routeParams,
75
+ hash === undefined ? undefined : { hash },
76
+ ),
77
+ [router, routeName, routeParams, hash],
65
78
  );
66
79
 
67
80
  const handleClick = useCallback(
@@ -79,9 +92,15 @@ const LinkImpl: FC<LinkProps> = ({
79
92
  }
80
93
 
81
94
  evt.preventDefault();
82
- router.navigate(routeName, routeParams, routeOptions).catch(() => {});
95
+ navigateWithHash(
96
+ router,
97
+ routeName,
98
+ routeParams,
99
+ hash,
100
+ routeOptions,
101
+ ).catch(() => {});
83
102
  },
84
- [onClick, target, router, routeName, routeParams, routeOptions],
103
+ [onClick, target, router, routeName, routeParams, routeOptions, hash],
85
104
  );
86
105
 
87
106
  const finalClassName = buildActiveClassName(
@@ -10,16 +10,26 @@ export function useIsActiveRoute(
10
10
  params?: Params,
11
11
  strict = false,
12
12
  ignoreQueryParams = true,
13
+ hash?: string,
13
14
  ): boolean {
14
15
  const router = useRouter();
15
16
 
16
17
  // createActiveRouteSource is per-router + canonical-args cached in
17
18
  // @real-router/sources, so passing params by reference is safe — equivalent
18
- // param shapes hit the same cache entry regardless of key order.
19
- const store = createActiveRouteSource(router, routeName, params, {
20
- strict,
21
- ignoreQueryParams,
22
- });
19
+ // param shapes hit the same cache entry regardless of key order. The
20
+ // `hash` argument (#532) is part of the cache key when defined: a Link
21
+ // pointing to `/settings#account` shares its source only with other
22
+ // consumers using the same routeName + params + hash.
23
+ // exactOptionalPropertyTypes forbids `{ hash: undefined }` literally, so
24
+ // we conditionally include the key only when the caller passed a value.
25
+ const store = createActiveRouteSource(
26
+ router,
27
+ routeName,
28
+ params,
29
+ hash === undefined
30
+ ? { strict, ignoreQueryParams }
31
+ : { strict, ignoreQueryParams, hash },
32
+ );
23
33
 
24
34
  return useSyncExternalStore(
25
35
  store.subscribe,
package/src/types.ts CHANGED
@@ -24,6 +24,16 @@ export interface LinkProps<
24
24
  activeClassName?: string;
25
25
  activeStrict?: boolean;
26
26
  ignoreQueryParams?: boolean;
27
+ /**
28
+ * URL fragment (decoded form, no leading "#") (#532).
29
+ * - omitted/`undefined` → preserve current fragment on same-route navigation
30
+ * - `""` → clear fragment
31
+ * - non-empty → set fragment
32
+ *
33
+ * Requires a URL plugin (browser-plugin or navigation-plugin) for full
34
+ * round-trip; hash-plugin ignores the prop with a one-time dev warning.
35
+ */
36
+ hash?: string;
27
37
  target?: string;
28
38
  onClick?: MouseEventHandler<HTMLAnchorElement>;
29
39
  onMouseOver?: MouseEventHandler<HTMLAnchorElement>;
@@ -1,2 +0,0 @@
1
- const e=require(`./RouterProvider-XuokeTtL.js`);let t=require(`react`),n=require(`react/jsx-runtime`);function r(t,n){return t.routeName===n.routeName&&t.className===n.className&&t.activeClassName===n.activeClassName&&t.activeStrict===n.activeStrict&&t.ignoreQueryParams===n.ignoreQueryParams&&t.onClick===n.onClick&&t.target===n.target&&t.style===n.style&&t.children===n.children&&e.u(t.routeParams,n.routeParams)&&e.u(t.routeOptions,n.routeOptions)}const i=(0,t.memo)(({routeName:r,routeParams:i=e.p,routeOptions:a=e.f,className:o,activeClassName:s=`active`,activeStrict:c=!1,ignoreQueryParams:l=!0,onClick:u,target:d,children:f,...p})=>{let m=e.h(),h=e.s(r,i,c,l),g=(0,t.useMemo)(()=>e.l(m,r,i),[m,r,i]),_=(0,t.useCallback)(t=>{u&&(u(t),t.defaultPrevented)||!e.d(t.nativeEvent)||d===`_blank`||(t.preventDefault(),m.navigate(r,i,a).catch(()=>{}))},[u,d,m,r,i,a]),v=e.c(h,s,o);return(0,n.jsx)(`a`,{...p,href:g,className:v,onClick:_,children:f})},r);i.displayName=`Link`,Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
2
- //# sourceMappingURL=Link-BzzSlRBj.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Link-BzzSlRBj.js","names":["shallowEqual","EMPTY_PARAMS","EMPTY_OPTIONS","useRouter","useIsActiveRoute","buildHref","shouldNavigate","buildActiveClassName"],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n shallowEqual,\n} from \"../dom-utils\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { LinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\nfunction areLinkPropsEqual(\n prev: Readonly<LinkProps>,\n next: Readonly<LinkProps>,\n): boolean {\n return (\n prev.routeName === next.routeName &&\n prev.className === next.className &&\n prev.activeClassName === next.activeClassName &&\n prev.activeStrict === next.activeStrict &&\n prev.ignoreQueryParams === next.ignoreQueryParams &&\n prev.onClick === next.onClick &&\n prev.target === next.target &&\n prev.style === next.style &&\n prev.children === next.children &&\n shallowEqual(prev.routeParams, next.routeParams) &&\n shallowEqual(prev.routeOptions, next.routeOptions)\n );\n}\n\nconst LinkImpl: FC<LinkProps> = ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n onClick,\n target,\n children,\n ...props\n}) => {\n // memo + areLinkPropsEqual guarantees that on bail-out the component does\n // not render; on render, routeParams/routeOptions either changed reference\n // (true change) or comparator failed (e.g., BigInt fallback to identity),\n // so they're safe to use directly in hook deps.\n\n const router = useRouter();\n\n const isActive = useIsActiveRoute(\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n const href = useMemo(\n () => buildHref(router, routeName, routeParams),\n [router, routeName, routeParams],\n );\n\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n if (onClick) {\n onClick(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt.nativeEvent) || target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n router.navigate(routeName, routeParams, routeOptions).catch(() => {});\n },\n [onClick, target, router, routeName, routeParams, routeOptions],\n );\n\n const finalClassName = buildActiveClassName(\n isActive,\n activeClassName,\n className,\n );\n\n return (\n <a {...props} href={href} className={finalClassName} onClick={handleClick}>\n {children}\n </a>\n );\n};\n\nexport const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);\n\nLink.displayName = \"Link\";\n"],"mappings":"sGAeA,SAAS,EACP,EACA,EACS,CACT,OACE,EAAK,YAAc,EAAK,WACxB,EAAK,YAAc,EAAK,WACxB,EAAK,kBAAoB,EAAK,iBAC9B,EAAK,eAAiB,EAAK,cAC3B,EAAK,oBAAsB,EAAK,mBAChC,EAAK,UAAY,EAAK,SACtB,EAAK,SAAW,EAAK,QACrB,EAAK,QAAU,EAAK,OACpB,EAAK,WAAa,EAAK,UACvBA,EAAAA,EAAa,EAAK,YAAa,EAAK,YAAY,EAChDA,EAAAA,EAAa,EAAK,aAAc,EAAK,aAAa,CAqEtD,MAAa,GAAA,EAAA,EAAA,OAjEoB,CAC/B,YACA,cAAcC,EAAAA,EACd,eAAeC,EAAAA,EACf,YACA,kBAAkB,SAClB,eAAe,GACf,oBAAoB,GACpB,UACA,SACA,WACA,GAAG,KACC,CAMJ,IAAM,EAASC,EAAAA,GAAW,CAEpB,EAAWC,EAAAA,EACf,EACA,EACA,EACA,EACD,CAEK,GAAA,EAAA,EAAA,aACEC,EAAAA,EAAU,EAAQ,EAAW,EAAY,CAC/C,CAAC,EAAQ,EAAW,EAAY,CACjC,CAEK,GAAA,EAAA,EAAA,aACH,GAAuC,CAClC,IACF,EAAQ,EAAI,CAER,EAAI,mBAKN,CAACC,EAAAA,EAAe,EAAI,YAAY,EAAI,IAAW,WAInD,EAAI,gBAAgB,CACpB,EAAO,SAAS,EAAW,EAAa,EAAa,CAAC,UAAY,GAAG,GAEvE,CAAC,EAAS,EAAQ,EAAQ,EAAW,EAAa,EAAa,CAChE,CAEK,EAAiBC,EAAAA,EACrB,EACA,EACA,EACD,CAED,OACE,EAAA,EAAA,KAAC,IAAD,CAAG,GAAI,EAAa,OAAM,UAAW,EAAgB,QAAS,EAC3D,WACC,CAAA,EAI0C,EAAkB,CAEpE,EAAK,YAAc"}
@@ -1,2 +0,0 @@
1
- let e=require(`react`),t=require(`@real-router/core`),n=require(`@real-router/route-utils`),r=require(`react/jsx-runtime`),i=require(`@real-router/sources`),a=require(`@real-router/core/api`);const o=(0,e.createContext)(null),s=(0,e.createContext)(null),c=(0,e.createContext)(null),l=()=>{let t=(0,e.useContext)(s);if(!t)throw Error(`useRouter must be used within a RouterProvider`);return t};function u(n){let r=l(),a=(0,e.useMemo)(()=>(0,i.createRouteNodeSource)(r,n),[r,n]),o=(0,e.useSyncExternalStore)(a.subscribe,a.getSnapshot,a.getSnapshot),s=(0,t.getNavigator)(r);return(0,e.useMemo)(()=>({navigator:s,route:o.route,previousRoute:o.previousRoute}),[s,o])}const d=Object.freeze({}),f=Object.freeze({}),p=`data-real-router-announcer`;function m(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=h(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),v(t)},f=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,d(e,document.querySelector(`h1`))}},100),p=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=_(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}d(i,t)}})})});return{destroy(){o=!0,p(),clearTimeout(l),clearTimeout(f),g()}}}function h(){let e=document.querySelector(`[${p}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(p,``),document.body.prepend(t),t}function g(){document.querySelector(`[${p}]`)?.remove()}function _(e,t,n,r){if(n)return n(e);let i=(r?.textContent??``).trim(),a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function v(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const y=`real-router:scroll`,b=Object.freeze({destroy:()=>{}});function x(e,t){if(globalThis.window===void 0)return b;let n=t?.mode??`restore`;if(n===`manual`)return b;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let o=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},s=e=>{let t=i?.();t?t.scrollTop=e:globalThis.scrollTo(0,e)},c=()=>{let e=globalThis.location.hash;if(r&&e.length>1){let t;try{t=decodeURIComponent(e.slice(1))}catch{t=e.slice(1)}let n=document.getElementById(t);if(n){n.scrollIntoView();return}}s(0)},l=!1,u=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;t&&w(S(t),o()),requestAnimationFrame(()=>{if(!l){if(n===`top`||!r){c();return}if(r.navigationType!==`replace`){if(r.direction===`back`||r.navigationType===`traverse`||r.navigationType===`reload`){s(C()[S(e)]??0);return}c()}}})}),d=()=>{let t=e.getState();t&&w(S(t),o())};return globalThis.addEventListener(`pagehide`,d),{destroy:()=>{if(!l){l=!0,u(),globalThis.removeEventListener(`pagehide`,d);try{history.scrollRestoration=a}catch{}}}}}function S(e){return`${e.name}:${T(e.params)}`}function C(){try{let e=sessionStorage.getItem(y);return e?JSON.parse(e):{}}catch{return{}}}function w(e,t){try{let n=C();n[e]=t,sessionStorage.setItem(y,JSON.stringify(n))}catch{}}function T(e){return JSON.stringify(e,E)}function E(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const D=Object.freeze({destroy:()=>{}});function O(e){if(typeof document>`u`||typeof document.startViewTransition!=`function`)return D;let t=null,n=null,r=!1,i=()=>{t?.(),t=null},a=e.subscribeLeave(({signal:e})=>{if(!e.aborted)return r=!1,i(),new Promise(a=>{let o=new Promise(e=>{t=e});e.addEventListener(`abort`,()=>{r||(i(),n?.skipTransition?.(),a())},{once:!0});try{n=document.startViewTransition(()=>(a(),o))}catch{i(),a()}})}),o=e.subscribe(()=>{let e=t;r=!0,t=null,e===null?n=null:setTimeout(()=>{e(),n=null},0)});return{destroy:()=>{a(),o(),n?.skipTransition?.(),n=null,i()}}}function k(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function A(e,t,n){try{let r=e.buildUrl;if(r){let e=r(t,n);if(e!==void 0)return e}return e.buildPath(t,n)}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function j(e){return e?e.match(/\S+/g)??[]:[]}function M(e,t,n){if(e&&t){let e=j(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=j(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function N(e,t){if(Object.is(e,t))return!0;if(!e||!t)return!1;let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;let r=e,i=t;for(let e of n)if(!Object.is(r[e],i[e]))return!1;return!0}function P(t,n,r=!1,a=!0){let o=(0,i.createActiveRouteSource)(l(),t,n,{strict:r,ignoreQueryParams:a});return(0,e.useSyncExternalStore)(o.subscribe,o.getSnapshot,o.getSnapshot)}function F({children:t,fallback:n,onError:a}){let o=(0,i.createDismissableError)(l()),s=(0,e.useSyncExternalStore)(o.subscribe,o.getSnapshot,o.getSnapshot),c=(0,e.useRef)(a);return c.current=a,(0,e.useEffect)(()=>{s.error&&c.current?.(s.error,s.toRoute,s.fromRoute)},[s.version]),(0,r.jsxs)(r.Fragment,{children:[t,s.error?n(s.error,s.resetError):null]})}const I=()=>{let t=(0,e.useContext)(c);if(!t)throw Error(`useNavigator must be used within a RouterProvider`);return t},L=()=>(0,n.getRouteUtils)((0,a.getPluginApi)(l()).getTree()),R=()=>{let t=(0,e.useContext)(o);if(!t)throw Error(`useRoute must be used within a RouterProvider`);if(!t.route)throw Error(`useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?`);return t};function z(){let t=(0,i.getTransitionSource)(l());return(0,e.useSyncExternalStore)(t.subscribe,t.getSnapshot,t.getSnapshot)}const B=({router:n,children:a,announceNavigation:l,scrollRestoration:u,viewTransitions:d})=>{(0,e.useEffect)(()=>{if(!l)return;let e=m(n);return()=>{e.destroy()}},[l,n]);let f=u?.mode,p=u?.anchorScrolling,h=u!==void 0;(0,e.useEffect)(()=>{if(!h)return;let e=x(n,{mode:f,anchorScrolling:p,scrollContainer:u.scrollContainer});return()=>{e.destroy()}},[n,h,f,p]),(0,e.useEffect)(()=>{if(!d)return;let e=O(n);return()=>{e.destroy()}},[n,d]);let g=(0,e.useMemo)(()=>(0,t.getNavigator)(n),[n]),_=(0,e.useMemo)(()=>(0,i.createRouteSource)(n),[n]),v=(0,e.useSyncExternalStore)(_.subscribe,_.getSnapshot,_.getSnapshot),y=(0,e.useMemo)(()=>({navigator:g,route:v.route,previousRoute:v.previousRoute}),[g,v]);return(0,r.jsx)(s.Provider,{value:n,children:(0,r.jsx)(c.Provider,{value:g,children:(0,r.jsx)(o.Provider,{value:y,children:a})})})};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return N}});
2
- //# sourceMappingURL=RouterProvider-XuokeTtL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RouterProvider-XuokeTtL.js","names":["NOOP_INSTANCE"],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/view-transitions.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouterTransition.tsx","../../src/RouterProvider.tsx"],"sourcesContent":["// packages/react/src/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/src/hooks/useRouter.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = useContext(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const store = useMemo(\n () => createRouteNodeSource(router, nodeName),\n [router, nodeName],\n );\n\n // Use snapshot reference directly. createRouteNodeSource via stabilizeState\n // returns the SAME snapshot when the node-relevant state did not change,\n // so memoization on `[navigator, snapshot]` preserves identity for consumers.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n // getNavigator is WeakMap-cached in core; additional useMemo is redundant.\n const navigator = getNavigator(router);\n\n return useMemo(\n (): RouteContext => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n}\n","// packages/react/src/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","import type { Router, State } from \"@real-router/core\";\n\nconst CLEAR_DELAY = 7000;\nconst SAFARI_READY_DELAY = 100;\nconst ANNOUNCER_ATTR = \"data-real-router-announcer\";\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0\";\n\nexport interface RouteAnnouncerOptions {\n prefix?: string;\n getAnnouncementText?: (route: State) => string;\n}\n\nexport function createRouteAnnouncer(\n router: Router,\n options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n const prefix = options?.prefix ?? \"Navigated to \";\n const getCustomText = options?.getAnnouncementText;\n\n let isInitialNavigation = true;\n let isReady = false;\n let isDestroyed = false;\n let lastAnnouncedText = \"\";\n let pendingText: string | null = null;\n let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const doAnnounce = (text: string, h1: HTMLElement | null): void => {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus(h1);\n };\n\n // Safari-ready delay: announcing before VoiceOver wires up the aria-live region\n // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms\n // before marking the announcer \"ready\" — any navigation during that window is\n // buffered in pendingText and flushed once the delay expires.\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n\n if (pendingText !== null && !isDestroyed) {\n const text = pendingText;\n\n pendingText = null;\n doAnnounce(text, document.querySelector<HTMLElement>(\"h1\"));\n }\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n // Double rAF: waits for two paint frames so the incoming route's DOM\n // (including the new <h1>) is fully rendered before resolveText reads it.\n // Single rAF fires before the new route's template has been attached,\n // which would cause resolveText to pick up the OLD h1 or fall back to\n // document.title / route.name prematurely.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n const text = resolveText(route, prefix, getCustomText, h1);\n\n if (!text || text === lastAnnouncedText) {\n return;\n }\n\n if (!isReady) {\n // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n pendingText = text;\n\n return;\n }\n\n doAnnounce(text, h1);\n });\n });\n });\n\n return {\n destroy() {\n isDestroyed = true;\n unsubscribe();\n clearTimeout(clearTimeoutId);\n clearTimeout(safariTimeoutId);\n removeAnnouncer();\n },\n };\n}\n\nfunction getOrCreateAnnouncer(): HTMLElement {\n const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n if (existing) {\n return existing;\n }\n\n const element = document.createElement(\"div\");\n\n element.setAttribute(\"style\", VISUALLY_HIDDEN);\n element.setAttribute(\"aria-live\", \"assertive\");\n element.setAttribute(\"aria-atomic\", \"true\");\n element.setAttribute(ANNOUNCER_ATTR, \"\");\n\n document.body.prepend(element);\n\n return element;\n}\n\nfunction removeAnnouncer(): void {\n document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();\n}\n\nfunction resolveText(\n route: State,\n prefix: string,\n getCustomText: ((route: State) => string) | undefined,\n h1: HTMLElement | null,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1Text = (h1?.textContent ?? \"\").trim();\n const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n ? \"\"\n : route.name;\n const rawText =\n h1Text || document.title || routeName || globalThis.location.pathname;\n\n return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n if (!h1) {\n return;\n }\n\n if (!h1.hasAttribute(\"tabindex\")) {\n h1.setAttribute(\"tabindex\", \"-1\");\n }\n\n h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst STORAGE_KEY = \"real-router:scroll\";\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"manual\";\n\nexport interface ScrollRestorationOptions {\n mode?: ScrollRestorationMode | undefined;\n anchorScrolling?: boolean | undefined;\n scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\ninterface NavigationContext {\n direction?: \"forward\" | \"back\" | \"unknown\";\n navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n router: Router,\n options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n if (typeof globalThis.window === \"undefined\") {\n return NOOP_INSTANCE;\n }\n\n const mode = options?.mode ?? \"restore\";\n\n // mode \"manual\" = utility does nothing. Don't flip history.scrollRestoration,\n // don't subscribe, don't register pagehide — leave the browser's native\n // auto-restore intact for the app to override if it wants to.\n if (mode === \"manual\") {\n return NOOP_INSTANCE;\n }\n\n const anchorEnabled = options?.anchorScrolling ?? true;\n const getContainer = options?.scrollContainer;\n\n const prevScrollRestoration = history.scrollRestoration;\n\n try {\n history.scrollRestoration = \"manual\";\n } catch {\n // Ignore — some embedded contexts may reject the assignment.\n }\n\n // Resolve the container lazily on every event so containers mounted AFTER\n // the provider still get correct scroll handling. Falls back to window when\n // the getter is absent or returns null (pre-mount).\n const readPos = (): number => {\n const element = getContainer?.();\n\n return element ? element.scrollTop : globalThis.scrollY;\n };\n\n const writePos = (top: number): void => {\n const element = getContainer?.();\n\n if (element) {\n element.scrollTop = top;\n } else {\n globalThis.scrollTo(0, top);\n }\n };\n\n const scrollToHashOrTop = (): void => {\n const hash = globalThis.location.hash;\n\n if (anchorEnabled && hash.length > 1) {\n // location.hash is percent-encoded; ids in the DOM are the raw string.\n // Decode for the match. Fall back to the raw slice if the hash contains\n // a malformed escape sequence (decodeURIComponent throws on those).\n let id: string;\n\n try {\n id = decodeURIComponent(hash.slice(1));\n } catch {\n id = hash.slice(1);\n }\n\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(id);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n };\n\n let destroyed = false;\n\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n const nav = (route.context as { navigation?: NavigationContext })\n .navigation;\n\n // Browsers dispatch reload as the initial navigation after refresh, so\n // previousRoute is undefined and capture is naturally skipped. The\n // pre-refresh position was already persisted via pagehide.\n if (previousRoute) {\n putPos(keyOf(previousRoute), readPos());\n }\n\n // Single rAF so DOM is committed before we read anchors / write scroll.\n // Guard against destroy() racing with the callback.\n requestAnimationFrame(() => {\n if (destroyed) {\n return;\n }\n\n if (mode === \"top\" || !nav) {\n scrollToHashOrTop();\n\n return;\n }\n\n if (nav.navigationType === \"replace\") {\n return;\n }\n\n if (\n nav.direction === \"back\" ||\n nav.navigationType === \"traverse\" ||\n nav.navigationType === \"reload\"\n ) {\n writePos(loadStore()[keyOf(route)] ?? 0);\n\n return;\n }\n\n scrollToHashOrTop();\n });\n });\n\n const onPageHide = (): void => {\n const current = router.getState();\n\n if (current) {\n putPos(keyOf(current), readPos());\n }\n };\n\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n return {\n destroy: () => {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n unsubscribe();\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n try {\n history.scrollRestoration = prevScrollRestoration;\n } catch {\n // Ignore.\n }\n },\n };\n}\n\nfunction keyOf(state: State): string {\n return `${state.name}:${canonicalJson(state.params)}`;\n}\n\nfunction loadStore(): Record<string, number> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n\n return raw ? (JSON.parse(raw) as Record<string, number>) : {};\n } catch {\n return {};\n }\n}\n\nfunction putPos(key: string, pos: number): void {\n try {\n const store = loadStore();\n\n store[key] = pos;\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // Ignore quota / security errors.\n }\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, canonicalReplacer);\n}\n\nfunction canonicalReplacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable\n const keys = Object.keys(val as Record<string, unknown>).sort(\n (left: string, right: string) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { Router } from \"@real-router/core\";\n\nexport interface ViewTransitions {\n destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ViewTransitions = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport function createViewTransitions(router: Router): ViewTransitions {\n if (\n typeof document === \"undefined\" ||\n typeof document.startViewTransition !== \"function\"\n ) {\n return NOOP_INSTANCE;\n }\n\n let closeVT: (() => void) | null = null;\n let currentVT: { skipTransition?: () => void } | null = null;\n // Tracks whether TRANSITION_SUCCESS fired for the current leave. Used to\n // distinguish \"benign cleanup abort\" (router's async path aborts its own\n // controller in a finally block after successful navigation) from \"real\n // cancellation\" (concurrent navigate, guard rejection, dispose).\n let successFired = false;\n\n const resolveAndClear = (): void => {\n closeVT?.();\n closeVT = null;\n };\n\n const offLeave = router.subscribeLeave(({ signal }) => {\n // Reentrant abort: signal already aborted when we're called. Open no VT\n // — router will fall through to TRANSITION_CANCELLED via isCurrentNav()\n // after leave resolves. addEventListener(\"abort\", ...) does not re-fire\n // for past events, so skipping startViewTransition is the safe path.\n if (signal.aborted) {\n return;\n }\n\n successFired = false;\n resolveAndClear();\n\n // Return a Promise so the router awaits until the browser invokes\n // updateCallback. This ensures old DOM snapshot is captured BEFORE the\n // router commits the new state — giving correct exit→state→entry\n // ordering (vs fire-and-forget, where URL changes before VT captures).\n return new Promise<void>((resolveLeave) => {\n // Capture the resolver synchronously BEFORE startViewTransition() is\n // called. The browser invokes updateCallback in a later task, but\n // router.subscribe (TRANSITION_SUCCESS) can fire before that. If we\n // captured `resolve` inside the callback, subscribe would see closeVT\n // still null and skip resolving — the deferred would hang for 4s\n // until the VT API aborts with TimeoutError.\n const deferred = new Promise<void>((resolve) => {\n closeVT = resolve;\n });\n\n signal.addEventListener(\n \"abort\",\n () => {\n if (successFired) {\n // Router's async path (#finishAsyncNavigation) aborts its own\n // controller in a finally block AFTER completeTransition (and\n // thus AFTER subscribe fired). This is cleanup, not\n // cancellation — VT is progressing normally, do nothing.\n return;\n }\n\n // Real cancellation (concurrent navigate, dispose). Resolve the\n // deferred so updateCallback can complete, skip the VT so no\n // stale animation leaks, and unblock the router if the abort\n // fires before updateCallback was invoked.\n resolveAndClear();\n currentVT?.skipTransition?.();\n resolveLeave();\n },\n { once: true },\n );\n\n try {\n currentVT = document.startViewTransition(() => {\n // Resolving here unblocks the router at the moment the browser\n // enters updateCallback — by spec, old DOM snapshot is captured\n // before this callback runs. Router now proceeds through\n // activation guards and setState; the VT animation waits on\n // `deferred`, which is resolved from router.subscribe after a\n // task-queue tick (see NOTE on setTimeout below).\n resolveLeave();\n\n return deferred;\n });\n } catch {\n // Defensive: spec says startViewTransition doesn't throw under\n // normal conditions, but Chromium has had edge cases (detached\n // document, extension interference). Clean up and unblock router.\n resolveAndClear();\n resolveLeave();\n }\n });\n });\n\n const offSuccess = router.subscribe(() => {\n const resolver = closeVT;\n\n successFired = true;\n closeVT = null;\n\n if (resolver === null) {\n currentVT = null;\n } else {\n // CRITICAL: CANNOT use requestAnimationFrame here. When the router\n // takes the async path (leave returned a Promise), subscribe fires\n // AFTER the browser has already transitioned VT into the\n // \"update-callback-called\" phase. In that phase Chromium sets\n // rendering suppression to true, which ALSO blocks rAF callbacks.\n // rAF would never fire → deferred never resolves → browser aborts\n // vt.ready with TimeoutError after 4s (observed in Chromium).\n //\n // setTimeout runs on the task queue independent of the rendering\n // pipeline, so it fires regardless of suppression. React's scheduler\n // uses MessageChannel tasks, which are queued before our setTimeout,\n // so the new DOM is committed by the time our callback runs.\n setTimeout(() => {\n resolver();\n currentVT = null;\n }, 0);\n }\n });\n\n return {\n destroy: () => {\n offLeave();\n offSuccess();\n currentVT?.skipTransition?.();\n currentVT = null;\n resolveAndClear();\n },\n };\n}\n","import type { Router, Params } from \"@real-router/core\";\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 &&\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\ntype BuildUrlFn = (name: string, params: Params) => string | undefined;\n\nexport function buildHref(\n router: Router,\n routeName: string,\n routeParams: Params,\n): string | undefined {\n try {\n const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n if (buildUrl) {\n const url = buildUrl(routeName, routeParams);\n\n if (url !== undefined) {\n return url;\n }\n }\n\n return router.buildPath(routeName, routeParams);\n } catch {\n console.error(\n `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n );\n\n return undefined;\n }\n}\n\nfunction parseTokens(value: string | undefined): string[] {\n return value ? (value.match(/\\S+/g) ?? []) : [];\n}\n\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n const activeTokens = parseTokens(activeClassName);\n\n if (activeTokens.length === 0) {\n return baseClassName ?? undefined;\n }\n if (!baseClassName) {\n return activeTokens.join(\" \");\n }\n\n const baseTokens = parseTokens(baseClassName);\n const seen = new Set(baseTokens);\n\n for (const token of activeTokens) {\n if (!seen.has(token)) {\n seen.add(token);\n baseTokens.push(token);\n }\n }\n\n return baseTokens.join(\" \");\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function shallowEqual(\n prev: object | undefined,\n next: object | undefined,\n): boolean {\n if (Object.is(prev, next)) {\n return true;\n }\n if (!prev || !next) {\n return false;\n }\n\n const prevKeys = Object.keys(prev);\n\n if (prevKeys.length !== Object.keys(next).length) {\n return false;\n }\n\n const prevRecord = prev as Record<string, unknown>;\n const nextRecord = next as Record<string, unknown>;\n\n for (const key of prevKeys) {\n if (!Object.is(prevRecord[key], nextRecord[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function applyLinkA11y(element: HTMLElement | null | undefined): void {\n if (!element) {\n return;\n }\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.hasAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Params } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n routeName: string,\n params?: Params,\n strict = false,\n ignoreQueryParams = true,\n): boolean {\n const router = useRouter();\n\n // createActiveRouteSource is per-router + canonical-args cached in\n // @real-router/sources, so passing params by reference is safe — equivalent\n // param shapes hit the same cache entry regardless of key order.\n const store = createActiveRouteSource(router, routeName, params, {\n strict,\n ignoreQueryParams,\n });\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { createDismissableError } from \"@real-router/sources\";\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { ReactNode, JSX } from \"react\";\n\nexport interface RouterErrorBoundaryProps {\n readonly children: ReactNode;\n readonly fallback: (error: RouterError, resetError: () => void) => ReactNode;\n readonly onError?: (\n error: RouterError,\n toRoute: State | null,\n fromRoute: State | null,\n ) => void;\n}\n\nexport function RouterErrorBoundary({\n children,\n fallback,\n onError,\n}: RouterErrorBoundaryProps): JSX.Element {\n const router = useRouter();\n const store = createDismissableError(router);\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n\n const onErrorRef = useRef(onError);\n\n // eslint-disable-next-line @eslint-react/refs -- \"latest ref\" pattern: sync callback to ref to avoid effect re-runs\n onErrorRef.current = onError;\n\n useEffect(() => {\n if (snapshot.error) {\n onErrorRef.current?.(\n snapshot.error,\n snapshot.toRoute,\n snapshot.fromRoute,\n );\n }\n // eslint-disable-next-line @eslint-react/exhaustive-deps -- onError tracked via ref, snapshot fields accessed inside callback\n }, [snapshot.version]);\n\n return (\n <>\n {children}\n {snapshot.error ? fallback(snapshot.error, snapshot.resetError) : null}\n </>\n );\n}\n","// packages/react/src/hooks/useNavigator.tsx\n\nimport { useContext } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = useContext(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getPluginApi } from \"@real-router/core/api\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside\n * `@real-router/route-utils`, so the same router always returns the same\n * `RouteUtils` instance across renders — no local cache needed in the adapter.\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n","// packages/react/src/hooks/useRoute.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\nimport type { Params, State } from \"@real-router/core\";\n\nexport const useRoute = <P extends Params = Params>(): Omit<\n RouteContextType<P>,\n \"route\"\n> & { route: State<P> } => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouterProvider\");\n }\n\n if (!routeContext.route) {\n throw new Error(\n \"useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?\",\n );\n }\n\n return routeContext as Omit<RouteContextType<P>, \"route\"> & {\n route: State<P>;\n };\n};\n","import { getTransitionSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\n\nexport function useRouterTransition(): RouterTransitionSnapshot {\n const router = useRouter();\n const store = getTransitionSource(router);\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { useEffect, useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\nimport {\n createRouteAnnouncer,\n createScrollRestoration,\n createViewTransitions,\n} from \"./dom-utils\";\n\nimport type { ScrollRestorationOptions } from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n announceNavigation?: boolean;\n scrollRestoration?: ScrollRestorationOptions;\n viewTransitions?: boolean;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\n scrollRestoration,\n viewTransitions,\n}) => {\n useEffect(() => {\n if (!announceNavigation) {\n return;\n }\n\n const announcer = createRouteAnnouncer(router);\n\n return () => {\n announcer.destroy();\n };\n }, [announceNavigation, router]);\n\n // Primitive deps so inline `{ mode: \"restore\" }` doesn't thrash on every\n // render. scrollContainer is a getter invoked lazily on every event inside\n // the utility — swapping its reference doesn't change the resolved element,\n // so we intentionally omit it from deps to keep inline getters stable.\n const srMode = scrollRestoration?.mode;\n const srAnchor = scrollRestoration?.anchorScrolling;\n const srEnabled = scrollRestoration !== undefined;\n\n useEffect(() => {\n if (!srEnabled) {\n return;\n }\n\n const sr = createScrollRestoration(router, {\n mode: srMode,\n anchorScrolling: srAnchor,\n // srEnabled check above guarantees scrollRestoration is defined.\n scrollContainer: scrollRestoration.scrollContainer,\n });\n\n return () => {\n sr.destroy();\n };\n // scrollRestoration (for scrollContainer) omitted — see comment above.\n // eslint-disable-next-line @eslint-react/exhaustive-deps\n }, [router, srEnabled, srMode, srAnchor]);\n\n useEffect(() => {\n if (!viewTransitions) {\n return;\n }\n\n const vt = createViewTransitions(router);\n\n return () => {\n vt.destroy();\n };\n }, [router, viewTransitions]);\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // useSyncExternalStore manages the router subscription lifecycle:\n // subscribe connects to router on first listener, unsubscribes on last.\n // This is Strict Mode safe — no useEffect cleanup needed.\n const store = useMemo(() => createRouteSource(router), [router]);\n // Use snapshot reference directly. createRouteSource via stabilizeState\n // returns the SAME snapshot reference when route.path is unchanged, so\n // useMemo below sees stable deps for idempotent navigations and\n // RouteContext consumers do not re-render.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const routeContextValue = useMemo(\n () => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n\n return (\n <RouterContext.Provider value={router}>\n <NavigatorContext.Provider value={navigator}>\n <RouteContext.Provider value={routeContextValue}>\n {children}\n </RouteContext.Provider>\n </NavigatorContext.Provider>\n </RouterContext.Provider>\n );\n};\n"],"mappings":"gMAOA,MAAa,GAAA,EAAA,EAAA,eAAsD,KAAK,CAE3D,GAAA,EAAA,EAAA,eAA6C,KAAK,CAElD,GAAA,EAAA,EAAA,eAAmD,KAAK,CCHxD,MAA0B,CACrC,IAAM,GAAA,EAAA,EAAA,YAAoB,EAAc,CAExC,GAAI,CAAC,EACH,MAAU,MAAM,iDAAiD,CAGnE,OAAO,GCPT,SAAgB,EAAa,EAAgC,CAC3D,IAAM,EAAS,GAAW,CAEpB,GAAA,EAAA,EAAA,cAAA,EAAA,EAAA,uBACwB,EAAQ,EAAS,CAC7C,CAAC,EAAQ,EAAS,CACnB,CAKK,GAAA,EAAA,EAAA,sBACJ,EAAM,UACN,EAAM,YACN,EAAM,YACP,CAGK,GAAA,EAAA,EAAA,cAAyB,EAAO,CAEtC,OAAA,EAAA,EAAA,cACuB,CACnB,YACA,MAAO,EAAS,MAChB,cAAe,EAAS,cACzB,EACD,CAAC,EAAW,EAAS,CACtB,CC9BH,MAAa,EAAe,OAAO,OAAO,EAAE,CAAC,CAKhC,EAAgB,OAAO,OAAO,EAAE,CAAC,CCNxC,EAAiB,6BAUvB,SAAgB,EACd,EACA,EACyB,CACzB,IAAM,EAAS,GAAS,QAAU,gBAC5B,EAAgB,GAAS,oBAE3B,EAAsB,GACtB,EAAU,GACV,EAAc,GACd,EAAoB,GACpB,EAA6B,KAC7B,EAEE,EAAY,GAAsB,CAElC,GAAc,EAAc,IAAiC,CACjE,EAAoB,EACpB,aAAa,EAAe,CAC5B,EAAU,YAAc,EACxB,EAAiB,eAAiB,CAChC,EAAU,YAAc,GACxB,EAAoB,IACnB,IAAY,CAEf,EAAY,EAAG,EAOX,EAAkB,eAAiB,CAGvC,GAFA,EAAU,GAEN,IAAgB,MAAQ,CAAC,EAAa,CACxC,IAAM,EAAO,EAEb,EAAc,KACd,EAAW,EAAM,SAAS,cAA2B,KAAK,CAAC,GAE5D,IAAmB,CAEhB,EAAc,EAAO,WAAW,CAAE,WAAY,CAClD,GAAI,EAAqB,CACvB,EAAsB,GAEtB,OAQF,0BAA4B,CAC1B,0BAA4B,CAC1B,GAAI,EACF,OAGF,IAAM,EAAK,SAAS,cAA2B,KAAK,CAC9C,EAAO,EAAY,EAAO,EAAQ,EAAe,EAAG,CAEtD,MAAC,GAAQ,IAAS,GAItB,IAAI,CAAC,EAAS,CAEZ,EAAc,EAEd,OAGF,EAAW,EAAM,EAAG,GACpB,EACF,EACF,CAEF,MAAO,CACL,SAAU,CACR,EAAc,GACd,GAAa,CACb,aAAa,EAAe,CAC5B,aAAa,EAAgB,CAC7B,GAAiB,EAEpB,CAGH,SAAS,GAAoC,CAC3C,IAAM,EAAW,SAAS,cAA2B,IAAI,EAAe,GAAG,CAE3E,GAAI,EACF,OAAO,EAGT,IAAM,EAAU,SAAS,cAAc,MAAM,CAS7C,OAPA,EAAQ,aAAa,QAAS,mJAAgB,CAC9C,EAAQ,aAAa,YAAa,YAAY,CAC9C,EAAQ,aAAa,cAAe,OAAO,CAC3C,EAAQ,aAAa,EAAgB,GAAG,CAExC,SAAS,KAAK,QAAQ,EAAQ,CAEvB,EAGT,SAAS,GAAwB,CAC/B,SAAS,cAAc,IAAI,EAAe,GAAG,EAAE,QAAQ,CAGzD,SAAS,EACP,EACA,EACA,EACA,EACQ,CACR,GAAI,EACF,OAAO,EAAc,EAAM,CAG7B,IAAM,GAAU,GAAI,aAAe,IAAI,MAAM,CACvC,EAAY,EAAM,KAAK,WAAW,KAAsB,CAC1D,GACA,EAAM,KAIV,MAAO,GAAG,IAFR,GAAU,SAAS,OAAS,GAAa,WAAW,SAAS,WAKjE,SAAS,EAAY,EAA8B,CAC5C,IAIA,EAAG,aAAa,WAAW,EAC9B,EAAG,aAAa,WAAY,KAAK,CAGnC,EAAG,MAAM,CAAE,cAAe,GAAM,CAAC,EC3JnC,MAAM,EAAc,qBAEdA,EAAyC,OAAO,OAAO,CAC3D,YAAe,GAGhB,CAAC,CAeF,SAAgB,EACd,EACA,EACyB,CACzB,GAAW,WAAW,SAAW,OAC/B,OAAOA,EAGT,IAAM,EAAO,GAAS,MAAQ,UAK9B,GAAI,IAAS,SACX,OAAOA,EAGT,IAAM,EAAgB,GAAS,iBAAmB,GAC5C,EAAe,GAAS,gBAExB,EAAwB,QAAQ,kBAEtC,GAAI,CACF,QAAQ,kBAAoB,cACtB,EAOR,IAAM,MAAwB,CAC5B,IAAM,EAAU,KAAgB,CAEhC,OAAO,EAAU,EAAQ,UAAY,WAAW,SAG5C,EAAY,GAAsB,CACtC,IAAM,EAAU,KAAgB,CAE5B,EACF,EAAQ,UAAY,EAEpB,WAAW,SAAS,EAAG,EAAI,EAIzB,MAAgC,CACpC,IAAM,EAAO,WAAW,SAAS,KAEjC,GAAI,GAAiB,EAAK,OAAS,EAAG,CAIpC,IAAI,EAEJ,GAAI,CACF,EAAK,mBAAmB,EAAK,MAAM,EAAE,CAAC,MAChC,CACN,EAAK,EAAK,MAAM,EAAE,CAIpB,IAAM,EAAU,SAAS,eAAe,EAAG,CAE3C,GAAI,EAAS,CACX,EAAQ,gBAAgB,CAExB,QAIJ,EAAS,EAAE,EAGT,EAAY,GAEV,EAAc,EAAO,WAAW,CAAE,QAAO,mBAAoB,CACjE,IAAM,EAAO,EAAM,QAChB,WAKC,GACF,EAAO,EAAM,EAAc,CAAE,GAAS,CAAC,CAKzC,0BAA4B,CACtB,MAIJ,IAAI,IAAS,OAAS,CAAC,EAAK,CAC1B,GAAmB,CAEnB,OAGE,KAAI,iBAAmB,UAI3B,IACE,EAAI,YAAc,QAClB,EAAI,iBAAmB,YACvB,EAAI,iBAAmB,SACvB,CACA,EAAS,GAAW,CAAC,EAAM,EAAM,GAAK,EAAE,CAExC,OAGF,GAAmB,IACnB,EACF,CAEI,MAAyB,CAC7B,IAAM,EAAU,EAAO,UAAU,CAE7B,GACF,EAAO,EAAM,EAAQ,CAAE,GAAS,CAAC,EAMrC,OAFA,WAAW,iBAAiB,WAAY,EAAW,CAE5C,CACL,YAAe,CACT,MAMJ,CAFA,EAAY,GACZ,GAAa,CACb,WAAW,oBAAoB,WAAY,EAAW,CAEtD,GAAI,CACF,QAAQ,kBAAoB,OACtB,KAIX,CAGH,SAAS,EAAM,EAAsB,CACnC,MAAO,GAAG,EAAM,KAAK,GAAG,EAAc,EAAM,OAAO,GAGrD,SAAS,GAAoC,CAC3C,GAAI,CACF,IAAM,EAAM,eAAe,QAAQ,EAAY,CAE/C,OAAO,EAAO,KAAK,MAAM,EAAI,CAA8B,EAAE,MACvD,CACN,MAAO,EAAE,EAIb,SAAS,EAAO,EAAa,EAAmB,CAC9C,GAAI,CACF,IAAM,EAAQ,GAAW,CAEzB,EAAM,GAAO,EACb,eAAe,QAAQ,EAAa,KAAK,UAAU,EAAM,CAAC,MACpD,GAKV,SAAS,EAAc,EAAwB,CAC7C,OAAO,KAAK,UAAU,EAAO,EAAkB,CAGjD,SAAS,EAAkB,EAAc,EAAuB,CAC9D,GAAoB,OAAO,GAAQ,UAA/B,GAA2C,CAAC,MAAM,QAAQ,EAAI,CAAE,CAClE,IAAM,EAAkC,EAAE,CAEpC,EAAO,OAAO,KAAK,EAA+B,CAAC,MACtD,EAAc,IAAkB,EAAK,cAAc,EAAM,CAC3D,CAED,IAAK,IAAM,KAAO,EAChB,EAAO,GAAQ,EAAgC,GAGjD,OAAO,EAGT,OAAO,ECjNT,MAAM,EAAiC,OAAO,OAAO,CACnD,YAAe,GAGhB,CAAC,CAEF,SAAgB,EAAsB,EAAiC,CACrE,GACE,OAAO,SAAa,KACpB,OAAO,SAAS,qBAAwB,WAExC,OAAO,EAGT,IAAI,EAA+B,KAC/B,EAAoD,KAKpD,EAAe,GAEb,MAA8B,CAClC,KAAW,CACX,EAAU,MAGN,EAAW,EAAO,gBAAgB,CAAE,YAAa,CAKjD,MAAO,QAWX,MAPA,GAAe,GACf,GAAiB,CAMV,IAAI,QAAe,GAAiB,CAOzC,IAAM,EAAW,IAAI,QAAe,GAAY,CAC9C,EAAU,GACV,CAEF,EAAO,iBACL,YACM,CACA,IAYJ,GAAiB,CACjB,GAAW,kBAAkB,CAC7B,GAAc,GAEhB,CAAE,KAAM,GAAM,CACf,CAED,GAAI,CACF,EAAY,SAAS,yBAOnB,GAAc,CAEP,GACP,MACI,CAIN,GAAiB,CACjB,GAAc,GAEhB,EACF,CAEI,EAAa,EAAO,cAAgB,CACxC,IAAM,EAAW,EAEjB,EAAe,GACf,EAAU,KAEN,IAAa,KACf,EAAY,KAcZ,eAAiB,CACf,GAAU,CACV,EAAY,MACX,EAAE,EAEP,CAEF,MAAO,CACL,YAAe,CACb,GAAU,CACV,GAAY,CACZ,GAAW,kBAAkB,CAC7B,EAAY,KACZ,GAAiB,EAEpB,CC1IH,SAAgB,EAAe,EAA0B,CACvD,OACE,EAAI,SAAW,GACf,CAAC,EAAI,SACL,CAAC,EAAI,QACL,CAAC,EAAI,SACL,CAAC,EAAI,SAMT,SAAgB,EACd,EACA,EACA,EACoB,CACpB,GAAI,CACF,IAAM,EAAW,EAAO,SAExB,GAAI,EAAU,CACZ,IAAM,EAAM,EAAS,EAAW,EAAY,CAE5C,GAAI,IAAQ,IAAA,GACV,OAAO,EAIX,OAAO,EAAO,UAAU,EAAW,EAAY,MACzC,CACN,QAAQ,MACN,wBAAwB,EAAU,sEACnC,CAED,QAIJ,SAAS,EAAY,EAAqC,CACxD,OAAO,EAAS,EAAM,MAAM,OAAO,EAAI,EAAE,CAAI,EAAE,CAGjD,SAAgB,EACd,EACA,EACA,EACoB,CACpB,GAAI,GAAY,EAAiB,CAC/B,IAAM,EAAe,EAAY,EAAgB,CAEjD,GAAI,EAAa,SAAW,EAC1B,OAAO,GAAiB,IAAA,GAE1B,GAAI,CAAC,EACH,OAAO,EAAa,KAAK,IAAI,CAG/B,IAAM,EAAa,EAAY,EAAc,CACvC,EAAO,IAAI,IAAI,EAAW,CAEhC,IAAK,IAAM,KAAS,EACb,EAAK,IAAI,EAAM,GAClB,EAAK,IAAI,EAAM,CACf,EAAW,KAAK,EAAM,EAI1B,OAAO,EAAW,KAAK,IAAI,CAG7B,OAAO,GAAiB,IAAA,GAG1B,SAAgB,EACd,EACA,EACS,CACT,GAAI,OAAO,GAAG,EAAM,EAAK,CACvB,MAAO,GAET,GAAI,CAAC,GAAQ,CAAC,EACZ,MAAO,GAGT,IAAM,EAAW,OAAO,KAAK,EAAK,CAElC,GAAI,EAAS,SAAW,OAAO,KAAK,EAAK,CAAC,OACxC,MAAO,GAGT,IAAM,EAAa,EACb,EAAa,EAEnB,IAAK,IAAM,KAAO,EAChB,GAAI,CAAC,OAAO,GAAG,EAAW,GAAM,EAAW,GAAK,CAC9C,MAAO,GAIX,MAAO,GC9FT,SAAgB,EACd,EACA,EACA,EAAS,GACT,EAAoB,GACX,CAMT,IAAM,GAAA,EAAA,EAAA,yBALS,GAAW,CAKoB,EAAW,EAAQ,CAC/D,SACA,oBACD,CAAC,CAEF,OAAA,EAAA,EAAA,sBACE,EAAM,UACN,EAAM,YACN,EAAM,YACP,CCTH,SAAgB,EAAoB,CAClC,WACA,WACA,WACwC,CAExC,IAAM,GAAA,EAAA,EAAA,wBADS,GAAW,CACkB,CACtC,GAAA,EAAA,EAAA,sBACJ,EAAM,UACN,EAAM,YACN,EAAM,YACP,CAEK,GAAA,EAAA,EAAA,QAAoB,EAAQ,CAgBlC,MAbA,GAAW,QAAU,GAErB,EAAA,EAAA,eAAgB,CACV,EAAS,OACX,EAAW,UACT,EAAS,MACT,EAAS,QACT,EAAS,UACV,EAGF,CAAC,EAAS,QAAQ,CAAC,EAGpB,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,CACG,EACA,EAAS,MAAQ,EAAS,EAAS,MAAO,EAAS,WAAW,CAAG,KACjE,CAAA,CAAA,CC3CP,MAAa,MAAgC,CAC3C,IAAM,GAAA,EAAA,EAAA,YAAuB,EAAiB,CAE9C,GAAI,CAAC,EACH,MAAU,MAAM,oDAAoD,CAGtE,OAAO,GCeI,OAGX,EAAA,EAAA,gBAAA,EAAA,EAAA,cAFe,GAAW,CAEe,CAAC,SAAS,CAAC,CCxBzC,MAGc,CACzB,IAAM,GAAA,EAAA,EAAA,YAA0B,EAAa,CAE7C,GAAI,CAAC,EACH,MAAU,MAAM,gDAAgD,CAGlE,GAAI,CAAC,EAAa,MAChB,MAAU,MACR,oIACD,CAGH,OAAO,GClBT,SAAgB,GAAgD,CAE9D,IAAM,GAAA,EAAA,EAAA,qBADS,GAAW,CACe,CAEzC,OAAA,EAAA,EAAA,sBACE,EAAM,UACN,EAAM,YACN,EAAM,YACP,CCQH,MAAa,GAA0C,CACrD,SACA,WACA,qBACA,oBACA,qBACI,EACJ,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EACH,OAGF,IAAM,EAAY,EAAqB,EAAO,CAE9C,UAAa,CACX,EAAU,SAAS,GAEpB,CAAC,EAAoB,EAAO,CAAC,CAMhC,IAAM,EAAS,GAAmB,KAC5B,EAAW,GAAmB,gBAC9B,EAAY,IAAsB,IAAA,IAExC,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EACH,OAGF,IAAM,EAAK,EAAwB,EAAQ,CACzC,KAAM,EACN,gBAAiB,EAEjB,gBAAiB,EAAkB,gBACpC,CAAC,CAEF,UAAa,CACX,EAAG,SAAS,GAIb,CAAC,EAAQ,EAAW,EAAQ,EAAS,CAAC,EAEzC,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EACH,OAGF,IAAM,EAAK,EAAsB,EAAO,CAExC,UAAa,CACX,EAAG,SAAS,GAEb,CAAC,EAAQ,EAAgB,CAAC,CAE7B,IAAM,GAAA,EAAA,EAAA,cAAA,EAAA,EAAA,cAAuC,EAAO,CAAE,CAAC,EAAO,CAAC,CAKzD,GAAA,EAAA,EAAA,cAAA,EAAA,EAAA,mBAAwC,EAAO,CAAE,CAAC,EAAO,CAAC,CAK1D,GAAA,EAAA,EAAA,sBACJ,EAAM,UACN,EAAM,YACN,EAAM,YACP,CAEK,GAAA,EAAA,EAAA,cACG,CACL,YACA,MAAO,EAAS,MAChB,cAAe,EAAS,cACzB,EACD,CAAC,EAAW,EAAS,CACtB,CAED,OACE,EAAA,EAAA,KAAC,EAAc,SAAf,CAAwB,MAAO,YAC7B,EAAA,EAAA,KAAC,EAAiB,SAAlB,CAA2B,MAAO,YAChC,EAAA,EAAA,KAAC,EAAa,SAAd,CAAuB,MAAO,EAC3B,WACqB,CAAA,CACE,CAAA,CACL,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useRouterTransition-CjFqNYG6.d.ts","names":[],"sources":["../../src/types.ts","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useRouter.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useRouterTransition.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA,WAAuB,MAAA,GAAS,MAAA;EAC1C,SAAA,EAAW,SAAA;AAAA,IACT,UAAA,CAAW,CAAA;AAAA,UAEE,SAAA,WACL,MAAA,GAAS,MAAA,UACX,cAAA,CAAe,iBAAA;EACvB,SAAA;EACA,WAAA,GAAc,CAAA;EACd,YAAA,GAAe,iBAAA;EACf,eAAA;EACA,YAAA;EACA,iBAAA;EACA,MAAA;EACA,OAAA,GAAU,iBAAA,CAAkB,iBAAA;EAC5B,WAAA,GAAc,iBAAA,CAAkB,iBAAA;AAAA;;;UCpBjB,wBAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,KAAA,EAAO,WAAA,EAAa,UAAA,iBAA2B,SAAA;EAAA,SAC1D,OAAA,IACP,KAAA,EAAO,WAAA,EACP,OAAA,EAAS,KAAA,SACT,SAAA,EAAW,KAAA;AAAA;AAAA,iBAIC,mBAAA,CAAA;EACd,QAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,GAAA,CAAI,OAAA;;;cCdrB,SAAA,QAAgB,MAAA;;;cCAhB,YAAA,QAAmB,SAAA;;;;;;;;AHAhC;;;;;;;;;;;;;;;;;;cIsBa,aAAA,QAAoB,UAAA;;;cCrBpB,QAAA,aAAsB,MAAA,GAAS,MAAA,OAAW,IAAA,CACrD,YAAA,CAAiB,CAAA;EAEb,KAAA,EAAO,KAAA,CAAM,CAAA;AAAA;;;iBCJH,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA"}
@@ -1,2 +0,0 @@
1
- import{c as e,d as t,f as n,h as r,l as i,p as a,s as o,u as s}from"./RouterProvider-D84Tsw-s.mjs";import{memo as c,useCallback as l,useMemo as u}from"react";import{jsx as d}from"react/jsx-runtime";function f(e,t){return e.routeName===t.routeName&&e.className===t.className&&e.activeClassName===t.activeClassName&&e.activeStrict===t.activeStrict&&e.ignoreQueryParams===t.ignoreQueryParams&&e.onClick===t.onClick&&e.target===t.target&&e.style===t.style&&e.children===t.children&&s(e.routeParams,t.routeParams)&&s(e.routeOptions,t.routeOptions)}const p=c(({routeName:s,routeParams:c=a,routeOptions:f=n,className:p,activeClassName:m=`active`,activeStrict:h=!1,ignoreQueryParams:g=!0,onClick:_,target:v,children:y,...b})=>{let x=r(),S=o(s,c,h,g),C=u(()=>i(x,s,c),[x,s,c]),w=l(e=>{_&&(_(e),e.defaultPrevented)||!t(e.nativeEvent)||v===`_blank`||(e.preventDefault(),x.navigate(s,c,f).catch(()=>{}))},[_,v,x,s,c,f]),T=e(S,m,p);return d(`a`,{...b,href:C,className:T,onClick:w,children:y})},f);p.displayName=`Link`;export{p as t};
2
- //# sourceMappingURL=Link-W2dQSXXI.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Link-W2dQSXXI.mjs","names":[],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n shallowEqual,\n} from \"../dom-utils\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { LinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\nfunction areLinkPropsEqual(\n prev: Readonly<LinkProps>,\n next: Readonly<LinkProps>,\n): boolean {\n return (\n prev.routeName === next.routeName &&\n prev.className === next.className &&\n prev.activeClassName === next.activeClassName &&\n prev.activeStrict === next.activeStrict &&\n prev.ignoreQueryParams === next.ignoreQueryParams &&\n prev.onClick === next.onClick &&\n prev.target === next.target &&\n prev.style === next.style &&\n prev.children === next.children &&\n shallowEqual(prev.routeParams, next.routeParams) &&\n shallowEqual(prev.routeOptions, next.routeOptions)\n );\n}\n\nconst LinkImpl: FC<LinkProps> = ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n onClick,\n target,\n children,\n ...props\n}) => {\n // memo + areLinkPropsEqual guarantees that on bail-out the component does\n // not render; on render, routeParams/routeOptions either changed reference\n // (true change) or comparator failed (e.g., BigInt fallback to identity),\n // so they're safe to use directly in hook deps.\n\n const router = useRouter();\n\n const isActive = useIsActiveRoute(\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n const href = useMemo(\n () => buildHref(router, routeName, routeParams),\n [router, routeName, routeParams],\n );\n\n const handleClick = useCallback(\n (evt: MouseEvent<HTMLAnchorElement>) => {\n if (onClick) {\n onClick(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt.nativeEvent) || target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n router.navigate(routeName, routeParams, routeOptions).catch(() => {});\n },\n [onClick, target, router, routeName, routeParams, routeOptions],\n );\n\n const finalClassName = buildActiveClassName(\n isActive,\n activeClassName,\n className,\n );\n\n return (\n <a {...props} href={href} className={finalClassName} onClick={handleClick}>\n {children}\n </a>\n );\n};\n\nexport const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);\n\nLink.displayName = \"Link\";\n"],"mappings":"sMAeA,SAAS,EACP,EACA,EACS,CACT,OACE,EAAK,YAAc,EAAK,WACxB,EAAK,YAAc,EAAK,WACxB,EAAK,kBAAoB,EAAK,iBAC9B,EAAK,eAAiB,EAAK,cAC3B,EAAK,oBAAsB,EAAK,mBAChC,EAAK,UAAY,EAAK,SACtB,EAAK,SAAW,EAAK,QACrB,EAAK,QAAU,EAAK,OACpB,EAAK,WAAa,EAAK,UACvB,EAAa,EAAK,YAAa,EAAK,YAAY,EAChD,EAAa,EAAK,aAAc,EAAK,aAAa,CAqEtD,MAAa,EAAsB,GAjEF,CAC/B,YACA,cAAc,EACd,eAAe,EACf,YACA,kBAAkB,SAClB,eAAe,GACf,oBAAoB,GACpB,UACA,SACA,WACA,GAAG,KACC,CAMJ,IAAM,EAAS,GAAW,CAEpB,EAAW,EACf,EACA,EACA,EACA,EACD,CAEK,EAAO,MACL,EAAU,EAAQ,EAAW,EAAY,CAC/C,CAAC,EAAQ,EAAW,EAAY,CACjC,CAEK,EAAc,EACjB,GAAuC,CAClC,IACF,EAAQ,EAAI,CAER,EAAI,mBAKN,CAAC,EAAe,EAAI,YAAY,EAAI,IAAW,WAInD,EAAI,gBAAgB,CACpB,EAAO,SAAS,EAAW,EAAa,EAAa,CAAC,UAAY,GAAG,GAEvE,CAAC,EAAS,EAAQ,EAAQ,EAAW,EAAa,EAAa,CAChE,CAEK,EAAiB,EACrB,EACA,EACA,EACD,CAED,OACE,EAAC,IAAD,CAAG,GAAI,EAAa,OAAM,UAAW,EAAgB,QAAS,EAC3D,WACC,CAAA,EAI0C,EAAkB,CAEpE,EAAK,YAAc"}
@@ -1,2 +0,0 @@
1
- import{createContext as e,useContext as t,useEffect as n,useMemo as r,useRef as i,useSyncExternalStore as a}from"react";import{getNavigator as o}from"@real-router/core";import{getRouteUtils as s}from"@real-router/route-utils";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import{createActiveRouteSource as d,createDismissableError as f,createRouteNodeSource as p,createRouteSource as m,getTransitionSource as h}from"@real-router/sources";import{getPluginApi as g}from"@real-router/core/api";const _=e(null),v=e(null),y=e(null),b=()=>{let e=t(v);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e};function x(e){let t=b(),n=r(()=>p(t,e),[t,e]),i=a(n.subscribe,n.getSnapshot,n.getSnapshot),s=o(t);return r(()=>({navigator:s,route:i.route,previousRoute:i.previousRoute}),[s,i])}const S=Object.freeze({}),C=Object.freeze({}),w=`data-real-router-announcer`;function T(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=E(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),k(t)},f=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,d(e,document.querySelector(`h1`))}},100),p=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=O(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}d(i,t)}})})});return{destroy(){o=!0,p(),clearTimeout(l),clearTimeout(f),D()}}}function E(){let e=document.querySelector(`[${w}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(w,``),document.body.prepend(t),t}function D(){document.querySelector(`[${w}]`)?.remove()}function O(e,t,n,r){if(n)return n(e);let i=(r?.textContent??``).trim(),a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function k(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const A=`real-router:scroll`,j=Object.freeze({destroy:()=>{}});function M(e,t){if(globalThis.window===void 0)return j;let n=t?.mode??`restore`;if(n===`manual`)return j;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let o=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},s=e=>{let t=i?.();t?t.scrollTop=e:globalThis.scrollTo(0,e)},c=()=>{let e=globalThis.location.hash;if(r&&e.length>1){let t;try{t=decodeURIComponent(e.slice(1))}catch{t=e.slice(1)}let n=document.getElementById(t);if(n){n.scrollIntoView();return}}s(0)},l=!1,u=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;t&&F(N(t),o()),requestAnimationFrame(()=>{if(!l){if(n===`top`||!r){c();return}if(r.navigationType!==`replace`){if(r.direction===`back`||r.navigationType===`traverse`||r.navigationType===`reload`){s(P()[N(e)]??0);return}c()}}})}),d=()=>{let t=e.getState();t&&F(N(t),o())};return globalThis.addEventListener(`pagehide`,d),{destroy:()=>{if(!l){l=!0,u(),globalThis.removeEventListener(`pagehide`,d);try{history.scrollRestoration=a}catch{}}}}}function N(e){return`${e.name}:${I(e.params)}`}function P(){try{let e=sessionStorage.getItem(A);return e?JSON.parse(e):{}}catch{return{}}}function F(e,t){try{let n=P();n[e]=t,sessionStorage.setItem(A,JSON.stringify(n))}catch{}}function I(e){return JSON.stringify(e,L)}function L(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const R=Object.freeze({destroy:()=>{}});function z(e){if(typeof document>`u`||typeof document.startViewTransition!=`function`)return R;let t=null,n=null,r=!1,i=()=>{t?.(),t=null},a=e.subscribeLeave(({signal:e})=>{if(!e.aborted)return r=!1,i(),new Promise(a=>{let o=new Promise(e=>{t=e});e.addEventListener(`abort`,()=>{r||(i(),n?.skipTransition?.(),a())},{once:!0});try{n=document.startViewTransition(()=>(a(),o))}catch{i(),a()}})}),o=e.subscribe(()=>{let e=t;r=!0,t=null,e===null?n=null:setTimeout(()=>{e(),n=null},0)});return{destroy:()=>{a(),o(),n?.skipTransition?.(),n=null,i()}}}function B(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function V(e,t,n){try{let r=e.buildUrl;if(r){let e=r(t,n);if(e!==void 0)return e}return e.buildPath(t,n)}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function H(e){return e?e.match(/\S+/g)??[]:[]}function U(e,t,n){if(e&&t){let e=H(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=H(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function W(e,t){if(Object.is(e,t))return!0;if(!e||!t)return!1;let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;let r=e,i=t;for(let e of n)if(!Object.is(r[e],i[e]))return!1;return!0}function G(e,t,n=!1,r=!0){let i=d(b(),e,t,{strict:n,ignoreQueryParams:r});return a(i.subscribe,i.getSnapshot,i.getSnapshot)}function K({children:e,fallback:t,onError:r}){let o=f(b()),s=a(o.subscribe,o.getSnapshot,o.getSnapshot),l=i(r);return l.current=r,n(()=>{s.error&&l.current?.(s.error,s.toRoute,s.fromRoute)},[s.version]),u(c,{children:[e,s.error?t(s.error,s.resetError):null]})}const q=()=>{let e=t(y);if(!e)throw Error(`useNavigator must be used within a RouterProvider`);return e},J=()=>s(g(b()).getTree()),Y=()=>{let e=t(_);if(!e)throw Error(`useRoute must be used within a RouterProvider`);if(!e.route)throw Error(`useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?`);return e};function X(){let e=h(b());return a(e.subscribe,e.getSnapshot,e.getSnapshot)}const Z=({router:e,children:t,announceNavigation:i,scrollRestoration:s,viewTransitions:c})=>{n(()=>{if(!i)return;let t=T(e);return()=>{t.destroy()}},[i,e]);let u=s?.mode,d=s?.anchorScrolling,f=s!==void 0;n(()=>{if(!f)return;let t=M(e,{mode:u,anchorScrolling:d,scrollContainer:s.scrollContainer});return()=>{t.destroy()}},[e,f,u,d]),n(()=>{if(!c)return;let t=z(e);return()=>{t.destroy()}},[e,c]);let p=r(()=>o(e),[e]),h=r(()=>m(e),[e]),g=a(h.subscribe,h.getSnapshot,h.getSnapshot),b=r(()=>({navigator:p,route:g.route,previousRoute:g.previousRoute}),[p,g]);return l(v.Provider,{value:e,children:l(y.Provider,{value:p,children:l(_.Provider,{value:b,children:t})})})};export{q as a,U as c,B as d,C as f,b as h,J as i,V as l,x as m,X as n,K as o,S as p,Y as r,G as s,Z as t,W as u};
2
- //# sourceMappingURL=RouterProvider-D84Tsw-s.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RouterProvider-D84Tsw-s.mjs","names":["NOOP_INSTANCE"],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/view-transitions.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouterTransition.tsx","../../src/RouterProvider.tsx"],"sourcesContent":["// packages/react/src/context.ts\n\nimport { createContext } from \"react\";\n\nimport type { RouteContext as RouteContextType } from \"./types\";\nimport type { Router, Navigator } from \"@real-router/core\";\n\nexport const RouteContext = createContext<RouteContextType | null>(null);\n\nexport const RouterContext = createContext<Router | null>(null);\n\nexport const NavigatorContext = createContext<Navigator | null>(null);\n","// packages/react/src/hooks/useRouter.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouterContext } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n const router = useContext(RouterContext);\n\n if (!router) {\n throw new Error(\"useRouter must be used within a RouterProvider\");\n }\n\n return router;\n};\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteNodeSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n const router = useRouter();\n\n const store = useMemo(\n () => createRouteNodeSource(router, nodeName),\n [router, nodeName],\n );\n\n // Use snapshot reference directly. createRouteNodeSource via stabilizeState\n // returns the SAME snapshot when the node-relevant state did not change,\n // so memoization on `[navigator, snapshot]` preserves identity for consumers.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n // getNavigator is WeakMap-cached in core; additional useMemo is redundant.\n const navigator = getNavigator(router);\n\n return useMemo(\n (): RouteContext => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n}\n","// packages/react/src/constants.ts\n\n/**\n * Stable empty object for default params\n */\nexport const EMPTY_PARAMS = Object.freeze({});\n\n/**\n * Stable empty options object\n */\nexport const EMPTY_OPTIONS = Object.freeze({});\n","import type { Router, State } from \"@real-router/core\";\n\nconst CLEAR_DELAY = 7000;\nconst SAFARI_READY_DELAY = 100;\nconst ANNOUNCER_ATTR = \"data-real-router-announcer\";\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0\";\n\nexport interface RouteAnnouncerOptions {\n prefix?: string;\n getAnnouncementText?: (route: State) => string;\n}\n\nexport function createRouteAnnouncer(\n router: Router,\n options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n const prefix = options?.prefix ?? \"Navigated to \";\n const getCustomText = options?.getAnnouncementText;\n\n let isInitialNavigation = true;\n let isReady = false;\n let isDestroyed = false;\n let lastAnnouncedText = \"\";\n let pendingText: string | null = null;\n let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const doAnnounce = (text: string, h1: HTMLElement | null): void => {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus(h1);\n };\n\n // Safari-ready delay: announcing before VoiceOver wires up the aria-live region\n // causes the first announcement to be silently dropped. Wait SAFARI_READY_DELAY ms\n // before marking the announcer \"ready\" — any navigation during that window is\n // buffered in pendingText and flushed once the delay expires.\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n\n if (pendingText !== null && !isDestroyed) {\n const text = pendingText;\n\n pendingText = null;\n doAnnounce(text, document.querySelector<HTMLElement>(\"h1\"));\n }\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n // Double rAF: waits for two paint frames so the incoming route's DOM\n // (including the new <h1>) is fully rendered before resolveText reads it.\n // Single rAF fires before the new route's template has been attached,\n // which would cause resolveText to pick up the OLD h1 or fall back to\n // document.title / route.name prematurely.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n const text = resolveText(route, prefix, getCustomText, h1);\n\n if (!text || text === lastAnnouncedText) {\n return;\n }\n\n if (!isReady) {\n // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n pendingText = text;\n\n return;\n }\n\n doAnnounce(text, h1);\n });\n });\n });\n\n return {\n destroy() {\n isDestroyed = true;\n unsubscribe();\n clearTimeout(clearTimeoutId);\n clearTimeout(safariTimeoutId);\n removeAnnouncer();\n },\n };\n}\n\nfunction getOrCreateAnnouncer(): HTMLElement {\n const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n if (existing) {\n return existing;\n }\n\n const element = document.createElement(\"div\");\n\n element.setAttribute(\"style\", VISUALLY_HIDDEN);\n element.setAttribute(\"aria-live\", \"assertive\");\n element.setAttribute(\"aria-atomic\", \"true\");\n element.setAttribute(ANNOUNCER_ATTR, \"\");\n\n document.body.prepend(element);\n\n return element;\n}\n\nfunction removeAnnouncer(): void {\n document.querySelector(`[${ANNOUNCER_ATTR}]`)?.remove();\n}\n\nfunction resolveText(\n route: State,\n prefix: string,\n getCustomText: ((route: State) => string) | undefined,\n h1: HTMLElement | null,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1Text = (h1?.textContent ?? \"\").trim();\n const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n ? \"\"\n : route.name;\n const rawText =\n h1Text || document.title || routeName || globalThis.location.pathname;\n\n return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n if (!h1) {\n return;\n }\n\n if (!h1.hasAttribute(\"tabindex\")) {\n h1.setAttribute(\"tabindex\", \"-1\");\n }\n\n h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst STORAGE_KEY = \"real-router:scroll\";\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"manual\";\n\nexport interface ScrollRestorationOptions {\n mode?: ScrollRestorationMode | undefined;\n anchorScrolling?: boolean | undefined;\n scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\ninterface NavigationContext {\n direction?: \"forward\" | \"back\" | \"unknown\";\n navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n router: Router,\n options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n if (typeof globalThis.window === \"undefined\") {\n return NOOP_INSTANCE;\n }\n\n const mode = options?.mode ?? \"restore\";\n\n // mode \"manual\" = utility does nothing. Don't flip history.scrollRestoration,\n // don't subscribe, don't register pagehide — leave the browser's native\n // auto-restore intact for the app to override if it wants to.\n if (mode === \"manual\") {\n return NOOP_INSTANCE;\n }\n\n const anchorEnabled = options?.anchorScrolling ?? true;\n const getContainer = options?.scrollContainer;\n\n const prevScrollRestoration = history.scrollRestoration;\n\n try {\n history.scrollRestoration = \"manual\";\n } catch {\n // Ignore — some embedded contexts may reject the assignment.\n }\n\n // Resolve the container lazily on every event so containers mounted AFTER\n // the provider still get correct scroll handling. Falls back to window when\n // the getter is absent or returns null (pre-mount).\n const readPos = (): number => {\n const element = getContainer?.();\n\n return element ? element.scrollTop : globalThis.scrollY;\n };\n\n const writePos = (top: number): void => {\n const element = getContainer?.();\n\n if (element) {\n element.scrollTop = top;\n } else {\n globalThis.scrollTo(0, top);\n }\n };\n\n const scrollToHashOrTop = (): void => {\n const hash = globalThis.location.hash;\n\n if (anchorEnabled && hash.length > 1) {\n // location.hash is percent-encoded; ids in the DOM are the raw string.\n // Decode for the match. Fall back to the raw slice if the hash contains\n // a malformed escape sequence (decodeURIComponent throws on those).\n let id: string;\n\n try {\n id = decodeURIComponent(hash.slice(1));\n } catch {\n id = hash.slice(1);\n }\n\n // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n const element = document.getElementById(id);\n\n if (element) {\n element.scrollIntoView();\n\n return;\n }\n }\n\n writePos(0);\n };\n\n let destroyed = false;\n\n const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n const nav = (route.context as { navigation?: NavigationContext })\n .navigation;\n\n // Browsers dispatch reload as the initial navigation after refresh, so\n // previousRoute is undefined and capture is naturally skipped. The\n // pre-refresh position was already persisted via pagehide.\n if (previousRoute) {\n putPos(keyOf(previousRoute), readPos());\n }\n\n // Single rAF so DOM is committed before we read anchors / write scroll.\n // Guard against destroy() racing with the callback.\n requestAnimationFrame(() => {\n if (destroyed) {\n return;\n }\n\n if (mode === \"top\" || !nav) {\n scrollToHashOrTop();\n\n return;\n }\n\n if (nav.navigationType === \"replace\") {\n return;\n }\n\n if (\n nav.direction === \"back\" ||\n nav.navigationType === \"traverse\" ||\n nav.navigationType === \"reload\"\n ) {\n writePos(loadStore()[keyOf(route)] ?? 0);\n\n return;\n }\n\n scrollToHashOrTop();\n });\n });\n\n const onPageHide = (): void => {\n const current = router.getState();\n\n if (current) {\n putPos(keyOf(current), readPos());\n }\n };\n\n globalThis.addEventListener(\"pagehide\", onPageHide);\n\n return {\n destroy: () => {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n unsubscribe();\n globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n try {\n history.scrollRestoration = prevScrollRestoration;\n } catch {\n // Ignore.\n }\n },\n };\n}\n\nfunction keyOf(state: State): string {\n return `${state.name}:${canonicalJson(state.params)}`;\n}\n\nfunction loadStore(): Record<string, number> {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n\n return raw ? (JSON.parse(raw) as Record<string, number>) : {};\n } catch {\n return {};\n }\n}\n\nfunction putPos(key: string, pos: number): void {\n try {\n const store = loadStore();\n\n store[key] = pos;\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(store));\n } catch {\n // Ignore quota / security errors.\n }\n}\n\nfunction canonicalJson(value: unknown): string {\n return JSON.stringify(value, canonicalReplacer);\n}\n\nfunction canonicalReplacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n // eslint-disable-next-line unicorn/no-array-sort -- ng-packagr uses pre-ES2023 lib; toSorted unavailable\n const keys = Object.keys(val as Record<string, unknown>).sort(\n (left: string, right: string) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { Router } from \"@real-router/core\";\n\nexport interface ViewTransitions {\n destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ViewTransitions = Object.freeze({\n destroy: () => {\n /* no-op */\n },\n});\n\nexport function createViewTransitions(router: Router): ViewTransitions {\n if (\n typeof document === \"undefined\" ||\n typeof document.startViewTransition !== \"function\"\n ) {\n return NOOP_INSTANCE;\n }\n\n let closeVT: (() => void) | null = null;\n let currentVT: { skipTransition?: () => void } | null = null;\n // Tracks whether TRANSITION_SUCCESS fired for the current leave. Used to\n // distinguish \"benign cleanup abort\" (router's async path aborts its own\n // controller in a finally block after successful navigation) from \"real\n // cancellation\" (concurrent navigate, guard rejection, dispose).\n let successFired = false;\n\n const resolveAndClear = (): void => {\n closeVT?.();\n closeVT = null;\n };\n\n const offLeave = router.subscribeLeave(({ signal }) => {\n // Reentrant abort: signal already aborted when we're called. Open no VT\n // — router will fall through to TRANSITION_CANCELLED via isCurrentNav()\n // after leave resolves. addEventListener(\"abort\", ...) does not re-fire\n // for past events, so skipping startViewTransition is the safe path.\n if (signal.aborted) {\n return;\n }\n\n successFired = false;\n resolveAndClear();\n\n // Return a Promise so the router awaits until the browser invokes\n // updateCallback. This ensures old DOM snapshot is captured BEFORE the\n // router commits the new state — giving correct exit→state→entry\n // ordering (vs fire-and-forget, where URL changes before VT captures).\n return new Promise<void>((resolveLeave) => {\n // Capture the resolver synchronously BEFORE startViewTransition() is\n // called. The browser invokes updateCallback in a later task, but\n // router.subscribe (TRANSITION_SUCCESS) can fire before that. If we\n // captured `resolve` inside the callback, subscribe would see closeVT\n // still null and skip resolving — the deferred would hang for 4s\n // until the VT API aborts with TimeoutError.\n const deferred = new Promise<void>((resolve) => {\n closeVT = resolve;\n });\n\n signal.addEventListener(\n \"abort\",\n () => {\n if (successFired) {\n // Router's async path (#finishAsyncNavigation) aborts its own\n // controller in a finally block AFTER completeTransition (and\n // thus AFTER subscribe fired). This is cleanup, not\n // cancellation — VT is progressing normally, do nothing.\n return;\n }\n\n // Real cancellation (concurrent navigate, dispose). Resolve the\n // deferred so updateCallback can complete, skip the VT so no\n // stale animation leaks, and unblock the router if the abort\n // fires before updateCallback was invoked.\n resolveAndClear();\n currentVT?.skipTransition?.();\n resolveLeave();\n },\n { once: true },\n );\n\n try {\n currentVT = document.startViewTransition(() => {\n // Resolving here unblocks the router at the moment the browser\n // enters updateCallback — by spec, old DOM snapshot is captured\n // before this callback runs. Router now proceeds through\n // activation guards and setState; the VT animation waits on\n // `deferred`, which is resolved from router.subscribe after a\n // task-queue tick (see NOTE on setTimeout below).\n resolveLeave();\n\n return deferred;\n });\n } catch {\n // Defensive: spec says startViewTransition doesn't throw under\n // normal conditions, but Chromium has had edge cases (detached\n // document, extension interference). Clean up and unblock router.\n resolveAndClear();\n resolveLeave();\n }\n });\n });\n\n const offSuccess = router.subscribe(() => {\n const resolver = closeVT;\n\n successFired = true;\n closeVT = null;\n\n if (resolver === null) {\n currentVT = null;\n } else {\n // CRITICAL: CANNOT use requestAnimationFrame here. When the router\n // takes the async path (leave returned a Promise), subscribe fires\n // AFTER the browser has already transitioned VT into the\n // \"update-callback-called\" phase. In that phase Chromium sets\n // rendering suppression to true, which ALSO blocks rAF callbacks.\n // rAF would never fire → deferred never resolves → browser aborts\n // vt.ready with TimeoutError after 4s (observed in Chromium).\n //\n // setTimeout runs on the task queue independent of the rendering\n // pipeline, so it fires regardless of suppression. React's scheduler\n // uses MessageChannel tasks, which are queued before our setTimeout,\n // so the new DOM is committed by the time our callback runs.\n setTimeout(() => {\n resolver();\n currentVT = null;\n }, 0);\n }\n });\n\n return {\n destroy: () => {\n offLeave();\n offSuccess();\n currentVT?.skipTransition?.();\n currentVT = null;\n resolveAndClear();\n },\n };\n}\n","import type { Router, Params } from \"@real-router/core\";\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n return (\n evt.button === 0 &&\n !evt.metaKey &&\n !evt.altKey &&\n !evt.ctrlKey &&\n !evt.shiftKey\n );\n}\n\ntype BuildUrlFn = (name: string, params: Params) => string | undefined;\n\nexport function buildHref(\n router: Router,\n routeName: string,\n routeParams: Params,\n): string | undefined {\n try {\n const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n if (buildUrl) {\n const url = buildUrl(routeName, routeParams);\n\n if (url !== undefined) {\n return url;\n }\n }\n\n return router.buildPath(routeName, routeParams);\n } catch {\n console.error(\n `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n );\n\n return undefined;\n }\n}\n\nfunction parseTokens(value: string | undefined): string[] {\n return value ? (value.match(/\\S+/g) ?? []) : [];\n}\n\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n const activeTokens = parseTokens(activeClassName);\n\n if (activeTokens.length === 0) {\n return baseClassName ?? undefined;\n }\n if (!baseClassName) {\n return activeTokens.join(\" \");\n }\n\n const baseTokens = parseTokens(baseClassName);\n const seen = new Set(baseTokens);\n\n for (const token of activeTokens) {\n if (!seen.has(token)) {\n seen.add(token);\n baseTokens.push(token);\n }\n }\n\n return baseTokens.join(\" \");\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function shallowEqual(\n prev: object | undefined,\n next: object | undefined,\n): boolean {\n if (Object.is(prev, next)) {\n return true;\n }\n if (!prev || !next) {\n return false;\n }\n\n const prevKeys = Object.keys(prev);\n\n if (prevKeys.length !== Object.keys(next).length) {\n return false;\n }\n\n const prevRecord = prev as Record<string, unknown>;\n const nextRecord = next as Record<string, unknown>;\n\n for (const key of prevKeys) {\n if (!Object.is(prevRecord[key], nextRecord[key])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function applyLinkA11y(element: HTMLElement | null | undefined): void {\n if (!element) {\n return;\n }\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.hasAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Params } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n routeName: string,\n params?: Params,\n strict = false,\n ignoreQueryParams = true,\n): boolean {\n const router = useRouter();\n\n // createActiveRouteSource is per-router + canonical-args cached in\n // @real-router/sources, so passing params by reference is safe — equivalent\n // param shapes hit the same cache entry regardless of key order.\n const store = createActiveRouteSource(router, routeName, params, {\n strict,\n ignoreQueryParams,\n });\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { createDismissableError } from \"@real-router/sources\";\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { ReactNode, JSX } from \"react\";\n\nexport interface RouterErrorBoundaryProps {\n readonly children: ReactNode;\n readonly fallback: (error: RouterError, resetError: () => void) => ReactNode;\n readonly onError?: (\n error: RouterError,\n toRoute: State | null,\n fromRoute: State | null,\n ) => void;\n}\n\nexport function RouterErrorBoundary({\n children,\n fallback,\n onError,\n}: RouterErrorBoundaryProps): JSX.Element {\n const router = useRouter();\n const store = createDismissableError(router);\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n\n const onErrorRef = useRef(onError);\n\n // eslint-disable-next-line @eslint-react/refs -- \"latest ref\" pattern: sync callback to ref to avoid effect re-runs\n onErrorRef.current = onError;\n\n useEffect(() => {\n if (snapshot.error) {\n onErrorRef.current?.(\n snapshot.error,\n snapshot.toRoute,\n snapshot.fromRoute,\n );\n }\n // eslint-disable-next-line @eslint-react/exhaustive-deps -- onError tracked via ref, snapshot fields accessed inside callback\n }, [snapshot.version]);\n\n return (\n <>\n {children}\n {snapshot.error ? fallback(snapshot.error, snapshot.resetError) : null}\n </>\n );\n}\n","// packages/react/src/hooks/useNavigator.tsx\n\nimport { useContext } from \"react\";\n\nimport { NavigatorContext } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n const navigator = useContext(NavigatorContext);\n\n if (!navigator) {\n throw new Error(\"useNavigator must be used within a RouterProvider\");\n }\n\n return navigator;\n};\n","import { getPluginApi } from \"@real-router/core/api\";\nimport { getRouteUtils } from \"@real-router/route-utils\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteUtils } from \"@real-router/route-utils\";\n\n/**\n * Returns a pre-computed {@link RouteUtils} instance for the current router.\n *\n * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside\n * `@real-router/route-utils`, so the same router always returns the same\n * `RouteUtils` instance across renders — no local cache needed in the adapter.\n *\n * @returns RouteUtils instance with pre-computed chains and siblings\n *\n * @example\n * ```tsx\n * const utils = useRouteUtils();\n *\n * utils.getChain(\"users.profile\");\n * // → [\"users\", \"users.profile\"]\n *\n * utils.getSiblings(\"users\");\n * // → [\"admin\"]\n *\n * utils.isDescendantOf(\"users.profile\", \"users\");\n * // → true\n * ```\n */\nexport const useRouteUtils = (): RouteUtils => {\n const router = useRouter();\n\n return getRouteUtils(getPluginApi(router).getTree());\n};\n","// packages/react/src/hooks/useRoute.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\nimport type { Params, State } from \"@real-router/core\";\n\nexport const useRoute = <P extends Params = Params>(): Omit<\n RouteContextType<P>,\n \"route\"\n> & { route: State<P> } => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouterProvider\");\n }\n\n if (!routeContext.route) {\n throw new Error(\n \"useRoute called with no active route. Did you forget to await router.start() before rendering, or is the router stopped/disposed?\",\n );\n }\n\n return routeContext as Omit<RouteContextType<P>, \"route\"> & {\n route: State<P>;\n };\n};\n","import { getTransitionSource } from \"@real-router/sources\";\nimport { useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\n\nexport function useRouterTransition(): RouterTransitionSnapshot {\n const router = useRouter();\n const store = getTransitionSource(router);\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { useEffect, useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\nimport {\n createRouteAnnouncer,\n createScrollRestoration,\n createViewTransitions,\n} from \"./dom-utils\";\n\nimport type { ScrollRestorationOptions } from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n router: Router;\n children: ReactNode;\n announceNavigation?: boolean;\n scrollRestoration?: ScrollRestorationOptions;\n viewTransitions?: boolean;\n}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\n scrollRestoration,\n viewTransitions,\n}) => {\n useEffect(() => {\n if (!announceNavigation) {\n return;\n }\n\n const announcer = createRouteAnnouncer(router);\n\n return () => {\n announcer.destroy();\n };\n }, [announceNavigation, router]);\n\n // Primitive deps so inline `{ mode: \"restore\" }` doesn't thrash on every\n // render. scrollContainer is a getter invoked lazily on every event inside\n // the utility — swapping its reference doesn't change the resolved element,\n // so we intentionally omit it from deps to keep inline getters stable.\n const srMode = scrollRestoration?.mode;\n const srAnchor = scrollRestoration?.anchorScrolling;\n const srEnabled = scrollRestoration !== undefined;\n\n useEffect(() => {\n if (!srEnabled) {\n return;\n }\n\n const sr = createScrollRestoration(router, {\n mode: srMode,\n anchorScrolling: srAnchor,\n // srEnabled check above guarantees scrollRestoration is defined.\n scrollContainer: scrollRestoration.scrollContainer,\n });\n\n return () => {\n sr.destroy();\n };\n // scrollRestoration (for scrollContainer) omitted — see comment above.\n // eslint-disable-next-line @eslint-react/exhaustive-deps\n }, [router, srEnabled, srMode, srAnchor]);\n\n useEffect(() => {\n if (!viewTransitions) {\n return;\n }\n\n const vt = createViewTransitions(router);\n\n return () => {\n vt.destroy();\n };\n }, [router, viewTransitions]);\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n // useSyncExternalStore manages the router subscription lifecycle:\n // subscribe connects to router on first listener, unsubscribes on last.\n // This is Strict Mode safe — no useEffect cleanup needed.\n const store = useMemo(() => createRouteSource(router), [router]);\n // Use snapshot reference directly. createRouteSource via stabilizeState\n // returns the SAME snapshot reference when route.path is unchanged, so\n // useMemo below sees stable deps for idempotent navigations and\n // RouteContext consumers do not re-render.\n const snapshot = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const routeContextValue = useMemo(\n () => ({\n navigator,\n route: snapshot.route,\n previousRoute: snapshot.previousRoute,\n }),\n [navigator, snapshot],\n );\n\n return (\n <RouterContext.Provider value={router}>\n <NavigatorContext.Provider value={navigator}>\n <RouteContext.Provider value={routeContextValue}>\n {children}\n </RouteContext.Provider>\n </NavigatorContext.Provider>\n </RouterContext.Provider>\n );\n};\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useRouterTransition-B3UWzXYl.d.mts","names":[],"sources":["../../src/types.ts","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useRouter.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRoute.tsx","../../src/hooks/useRouteNode.tsx","../../src/hooks/useRouterTransition.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA,WAAuB,MAAA,GAAS,MAAA;EAC1C,SAAA,EAAW,SAAA;AAAA,IACT,UAAA,CAAW,CAAA;AAAA,UAEE,SAAA,WACL,MAAA,GAAS,MAAA,UACX,cAAA,CAAe,iBAAA;EACvB,SAAA;EACA,WAAA,GAAc,CAAA;EACd,YAAA,GAAe,iBAAA;EACf,eAAA;EACA,YAAA;EACA,iBAAA;EACA,MAAA;EACA,OAAA,GAAU,iBAAA,CAAkB,iBAAA;EAC5B,WAAA,GAAc,iBAAA,CAAkB,iBAAA;AAAA;;;UCpBjB,wBAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,KAAA,EAAO,WAAA,EAAa,UAAA,iBAA2B,SAAA;EAAA,SAC1D,OAAA,IACP,KAAA,EAAO,WAAA,EACP,OAAA,EAAS,KAAA,SACT,SAAA,EAAW,KAAA;AAAA;AAAA,iBAIC,mBAAA,CAAA;EACd,QAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,GAAA,CAAI,OAAA;;;cCdrB,SAAA,QAAgB,MAAA;;;cCAhB,YAAA,QAAmB,SAAA;;;;;;;;AHAhC;;;;;;;;;;;;;;;;;;cIsBa,aAAA,QAAoB,UAAA;;;cCrBpB,QAAA,aAAsB,MAAA,GAAS,MAAA,OAAW,IAAA,CACrD,YAAA,CAAiB,CAAA;EAEb,KAAA,EAAO,KAAA,CAAM,CAAA;AAAA;;;iBCJH,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA"}