@real-router/react 0.15.1 → 0.16.1

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 (42) hide show
  1. package/README.md +12 -11
  2. package/dist/cjs/RouterProvider-DQ0bWmlq.js +2 -0
  3. package/dist/cjs/RouterProvider-DQ0bWmlq.js.map +1 -0
  4. package/dist/cjs/{RouterProvider-GwajvaK9.d.ts → RouterProvider-Ddv8Rs1U.d.ts} +4 -3
  5. package/dist/cjs/{RouterProvider-GwajvaK9.d.ts.map → RouterProvider-Ddv8Rs1U.d.ts.map} +1 -1
  6. package/dist/cjs/index.d.ts +1 -1
  7. package/dist/cjs/index.d.ts.map +1 -1
  8. package/dist/cjs/index.js +1 -1
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/legacy.d.ts +1 -1
  11. package/dist/cjs/legacy.js +1 -1
  12. package/dist/esm/{RouterProvider-BuGQIoZ-.d.mts → RouterProvider-B2KCZfL_.d.mts} +4 -3
  13. package/dist/esm/{RouterProvider-BuGQIoZ-.d.mts.map → RouterProvider-B2KCZfL_.d.mts.map} +1 -1
  14. package/dist/esm/RouterProvider-DqCTMwvh.mjs +2 -0
  15. package/dist/esm/RouterProvider-DqCTMwvh.mjs.map +1 -0
  16. package/dist/esm/index.d.mts +1 -1
  17. package/dist/esm/index.d.mts.map +1 -1
  18. package/dist/esm/index.mjs +1 -1
  19. package/dist/esm/index.mjs.map +1 -1
  20. package/dist/esm/legacy.d.mts +1 -1
  21. package/dist/esm/legacy.mjs +1 -1
  22. package/package.json +5 -5
  23. package/src/RouterProvider.tsx +18 -8
  24. package/src/components/Link.tsx +59 -62
  25. package/src/components/RouterErrorBoundary.tsx +11 -12
  26. package/src/components/modern/RouteView/RouteView.tsx +16 -6
  27. package/src/components/modern/RouteView/helpers.tsx +4 -0
  28. package/src/constants.ts +1 -1
  29. package/src/context.ts +1 -1
  30. package/src/hooks/useIsActiveRoute.tsx +9 -16
  31. package/src/hooks/useNavigator.tsx +1 -1
  32. package/src/hooks/useRoute.tsx +1 -1
  33. package/src/hooks/useRouteNode.tsx +12 -4
  34. package/src/hooks/useRouteUtils.tsx +3 -4
  35. package/src/hooks/useRouter.tsx +1 -1
  36. package/src/hooks/useRouterTransition.tsx +3 -4
  37. package/dist/cjs/RouterProvider-jqJCKUhp.js +0 -2
  38. package/dist/cjs/RouterProvider-jqJCKUhp.js.map +0 -1
  39. package/dist/esm/RouterProvider-DrAGH6K9.mjs +0 -2
  40. package/dist/esm/RouterProvider-DrAGH6K9.mjs.map +0 -1
  41. package/src/hooks/useRouterError.tsx +0 -29
  42. package/src/hooks/useStableValue.tsx +0 -29
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  npm install @real-router/react @real-router/core @real-router/browser-plugin
14
14
  ```
15
15
 
16
- **Peer dependency:** `react` >= 18.0.0
16
+ **Peer dependency:** main entry requires `react` >= 19.2.0 (uses `<Activity>`); `@real-router/react/legacy` works with `react` >= 18.0.0.
17
17
 
18
18
  ## Entry Points
19
19
 
@@ -51,10 +51,10 @@ function App() {
51
51
  <Link routeName="users">Users</Link>
52
52
  </nav>
53
53
  <RouteView nodeName="">
54
- <RouteView.Match routeName="home">
54
+ <RouteView.Match segment="home">
55
55
  <HomePage />
56
56
  </RouteView.Match>
57
- <RouteView.Match routeName="users">
57
+ <RouteView.Match segment="users">
58
58
  <UsersPage />
59
59
  </RouteView.Match>
60
60
  <RouteView.NotFound>
@@ -68,14 +68,14 @@ function App() {
68
68
 
69
69
  ## Hooks
70
70
 
71
- | Hook | Returns | Re-renders |
72
- | ----------------------- | ----------------------------------------- | --------------------------------------- |
73
- | `useRouter()` | `Router` | Never |
74
- | `useNavigator()` | `Navigator` | Never (stable ref, safe to destructure) |
75
- | `useRoute()` | `{ router, route, previousRoute }` | Every navigation |
76
- | `useRouteNode(name)` | `{ router, route, previousRoute }` | Only when node activates/deactivates |
77
- | `useRouteUtils()` | `RouteUtils` | Never |
78
- | `useRouterTransition()` | `{ isTransitioning, toRoute, fromRoute }` | On transition start/end |
71
+ | Hook | Returns | Re-renders |
72
+ | ----------------------- | ---------------------------------------------------------- | --------------------------------------- |
73
+ | `useRouter()` | `Router` | Never |
74
+ | `useNavigator()` | `Navigator` | Never (stable ref, safe to destructure) |
75
+ | `useRoute()` | `{ navigator, route, previousRoute }` | Every navigation |
76
+ | `useRouteNode(name)` | `{ navigator, route, previousRoute }` | Only when node activates/deactivates |
77
+ | `useRouteUtils()` | `RouteUtils` | Never |
78
+ | `useRouterTransition()` | `{ isTransitioning, isLeaveApproved, toRoute, fromRoute }` | On transition start/end |
79
79
 
80
80
  ```tsx
81
81
  // useRouteNode — re-renders only when "users.*" changes
@@ -149,6 +149,7 @@ Declarative route matching with optional `keepAlive` — preserves component sta
149
149
  | Prop | Type | Description |
150
150
  | ----------- | ----------- | --------------------------------------------------------------------------- |
151
151
  | `segment` | `string` | Route segment to match |
152
+ | `exact` | `boolean` | Exact match only — no descendants. Defaults to `false`. |
152
153
  | `keepAlive` | `boolean` | Preserve state via React `<Activity>` (React 19.2+) |
153
154
  | `fallback` | `ReactNode` | Shown while children suspend. Wraps children in `<Suspense>` when provided. |
154
155
 
@@ -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}))}function y(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function b(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 x(e){return e?e.match(/\S+/g)??[]:[]}function S(e,t,n){if(e&&t){let e=x(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=x(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 C(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 w(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 T(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&&C(e.routeParams,t.routeParams)&&C(e.routeOptions,t.routeOptions)}const E=(0,e.memo)(({routeName:t,routeParams:n=d,routeOptions:i=f,className:a,activeClassName:o=`active`,activeStrict:s=!1,ignoreQueryParams:c=!0,onClick:u,target:p,children:m,...h})=>{let g=l(),_=w(t,n,s,c),v=(0,e.useMemo)(()=>b(g,t,n),[g,t,n]),x=(0,e.useCallback)(e=>{u&&(u(e),e.defaultPrevented)||!y(e.nativeEvent)||p===`_blank`||(e.preventDefault(),g.navigate(t,n,i).catch(()=>{}))},[u,p,g,t,n,i]),C=S(_,o,a);return(0,r.jsx)(`a`,{...h,href:v,className:C,onClick:x,children:m})},T);E.displayName=`Link`;function D({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 O=()=>{let t=(0,e.useContext)(c);if(!t)throw Error(`useNavigator must be used within a RouterProvider`);return t},k=()=>(0,n.getRouteUtils)((0,a.getPluginApi)(l()).getTree()),A=()=>{let t=(0,e.useContext)(o);if(!t)throw Error(`useRoute must be used within a RouteProvider`);return t};function j(){let t=(0,i.getTransitionSource)(l());return(0,e.useSyncExternalStore)(t.subscribe,t.getSnapshot,t.getSnapshot)}const M=({router:n,children:a,announceNavigation:l})=>{(0,e.useEffect)(()=>{if(!l)return;let e=m(n);return()=>{e.destroy()}},[l,n]);let u=(0,e.useMemo)(()=>(0,t.getNavigator)(n),[n]),d=(0,e.useMemo)(()=>(0,i.createRouteSource)(n),[n]),f=(0,e.useSyncExternalStore)(d.subscribe,d.getSnapshot,d.getSnapshot),p=(0,e.useMemo)(()=>({navigator:u,route:f.route,previousRoute:f.previousRoute}),[u,f]);return(0,r.jsx)(s.Provider,{value:n,children:(0,r.jsx)(c.Provider,{value:u,children:(0,r.jsx)(o.Provider,{value:p,children:a})})})};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return M}});
2
+ //# sourceMappingURL=RouterProvider-DQ0bWmlq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterProvider-DQ0bWmlq.js","names":[],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/Link.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, 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 { 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/index.js\";\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","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\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\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 { createRouteAnnouncer } from \"./dom-utils/index.js\";\n\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}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\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 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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"}
@@ -48,8 +48,9 @@ declare const useNavigator: () => Navigator;
48
48
  /**
49
49
  * Returns a pre-computed {@link RouteUtils} instance for the current router.
50
50
  *
51
- * Internally retrieves the route tree via `getPluginApi` and delegates
52
- * to `getRouteUtils`, which caches instances per tree reference (WeakMap).
51
+ * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside
52
+ * `@real-router/route-utils`, so the same router always returns the same
53
+ * `RouteUtils` instance across renders — no local cache needed in the adapter.
53
54
  *
54
55
  * @returns RouteUtils instance with pre-computed chains and siblings
55
56
  *
@@ -87,4 +88,4 @@ interface RouteProviderProps {
87
88
  declare const RouterProvider: FC<RouteProviderProps>;
88
89
  //#endregion
89
90
  export { useRouteUtils as a, RouterErrorBoundary as c, LinkProps as d, useRoute as i, RouterErrorBoundaryProps as l, useRouterTransition as n, useNavigator as o, useRouteNode as r, useRouter as s, RouterProvider as t, Link as u };
90
- //# sourceMappingURL=RouterProvider-GwajvaK9.d.ts.map
91
+ //# sourceMappingURL=RouterProvider-Ddv8Rs1U.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterProvider-GwajvaK9.d.ts","names":[],"sources":["../../src/types.ts","../../src/components/Link.tsx","../../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","../../src/RouterProvider.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA;EACV,SAAA,EAAW,SAAA;AAAA,IACT,UAAA;AAAA,UAEa,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;;;cCMrB,IAAA,EAAM,EAAA,CAAG,SAAA;;;UC3BL,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;;;cCbrB,SAAA,QAAgB,MAAA;;;cCAhB,YAAA,QAAmB,SAAA;;;;;;;;AJAhC;;;;;;;;;;;;;;;;;cKuBa,aAAA,QAAoB,UAAA;;;cCvBpB,QAAA,QAAe,YAAA;;;iBCAZ,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA;;;UCGtB,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
1
+ {"version":3,"file":"RouterProvider-Ddv8Rs1U.d.ts","names":[],"sources":["../../src/types.ts","../../src/components/Link.tsx","../../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","../../src/RouterProvider.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA;EACV,SAAA,EAAW,SAAA;AAAA,IACT,UAAA;AAAA,UAEa,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;;;cCuErB,IAAA,EAAM,EAAA,CAAG,SAAA;;;UC3FL,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;;;;;;;;AJAhC;;;;;;;;;;;;;;;;;;cKsBa,aAAA,QAAoB,UAAA;;;cCtBpB,QAAA,QAAe,YAAA;;;iBCAZ,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA;;;UCGtB,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
@@ -1,4 +1,4 @@
1
- import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-GwajvaK9.js";
1
+ import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-Ddv8Rs1U.js";
2
2
  import { ReactElement, ReactNode } from "react";
3
3
  import { Navigator } from "@real-router/core";
4
4
  import { RouterTransitionSnapshot } from "@real-router/sources";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/components/modern/RouteView/types.ts","../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"mappings":";;;;;;UAEiB,cAAA;;WAEN,QAAA;;WAEA,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,UAAA;;WAEN,OAAA;EAPA;EAAA,SASA,KAAA;EAPU;EAAA,SASV,SAAA;EATmB;EAAA,SAWnB,QAAA,GAAW,SAAA;EARK;EAAA,SAUhB,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,aAAA;EATN;EAAA,SAWA,QAAA,EAAU,SAAA;AAAA;;;iBCtBL,KAAA,CAAM,MAAA,EAAQ,UAAA;AAAA,kBAAd,KAAA;EAAA,IAAK,WAAA;AAAA;AAAA,iBAML,QAAA,CAAS,MAAA,EAAQ,aAAA;AAAA,kBAAjB,QAAA;EAAA,IAAQ,WAAA;AAAA;;;iBCCf,aAAA,CAAA;EACP,QAAA;EACA;AAAA,GACC,QAAA,CAAS,cAAA,IAAkB,YAAA;AAAA,kBAHrB,aAAA;EAAA,IAAa,WAAA;AAAA;AAAA,cAgCT,SAAA,SAAS,aAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/components/modern/RouteView/types.ts","../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"mappings":";;;;;;UAEiB,cAAA;;WAEN,QAAA;;WAEA,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,UAAA;;WAEN,OAAA;EAPA;EAAA,SASA,KAAA;EAPU;EAAA,SASV,SAAA;EATmB;EAAA,SAWnB,QAAA,GAAW,SAAA;EARK;EAAA,SAUhB,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,aAAA;EATN;EAAA,SAWA,QAAA,EAAU,SAAA;AAAA;;;iBCtBL,KAAA,CAAM,MAAA,EAAQ,UAAA;AAAA,kBAAd,KAAA;EAAA,IAAK,WAAA;AAAA;AAAA,iBAML,QAAA,CAAS,MAAA,EAAQ,aAAA;AAAA,kBAAjB,QAAA;EAAA,IAAQ,WAAA;AAAA;;;iBCCf,aAAA,CAAA;EACP,QAAA;EACA;AAAA,GACC,QAAA,CAAS,cAAA,IAAkB,YAAA;AAAA,kBAHrB,aAAA;EAAA,IAAa,WAAA;AAAA;AAAA,cA0CT,SAAA,SAAS,aAAA"}
package/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-jqJCKUhp.js`);let t=require(`react`),n=require(`@real-router/core`),r=require(`@real-router/route-utils`),i=require(`react/jsx-runtime`);function a(e){return null}a.displayName=`RouteView.Match`;function o(e){return null}o.displayName=`RouteView.NotFound`;function s(e,t,n){return n?e===t:(0,r.startsWithSegment)(e,t)}function c(e,n){for(let r of t.Children.toArray(e))(0,t.isValidElement)(r)&&(r.type===a||r.type===o?n.push(r):c(r.props.children,n))}function l(e,n,r,a,o){let s=o===void 0?e:(0,i.jsx)(t.Suspense,{fallback:o,children:e});return r?(0,i.jsx)(t.Activity,{mode:a,children:s},n):(0,i.jsx)(t.Fragment,{children:s},n)}function u(e,r,a,c){let u=null,d=!1,f=[];for(let t of e){if(t.type===o){u=t.props.children;continue}let{segment:e,exact:n=!1,keepAlive:i=!1,fallback:p}=t.props,m=a?`${a}.${e}`:e;!d&&s(r,m,n)?(d=!0,c.add(m),f.push(l(t.props.children,m,i,`visible`,p))):i&&c.has(m)&&f.push(l(t.props.children,m,i,`hidden`,p))}return!d&&r===n.UNKNOWN_ROUTE&&u!==null&&f.push((0,i.jsx)(t.Fragment,{children:u},`__route-view-not-found__`)),{rendered:f,activeMatchFound:d}}function d({nodeName:n,children:r}){let{route:a}=e.c(n),o=(0,t.useRef)(new Set);if(!a)return null;let s=[];c(r,s);let{rendered:l}=u(s,a.name,n,o.current);return l.length>0?(0,i.jsx)(i.Fragment,{children:l}):null}d.displayName=`RouteView`;const f=Object.assign(d,{Match:a,NotFound:o});exports.Link=e.s,exports.RouteView=f,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.c,exports.useRouteUtils=e.i,exports.useRouter=e.l,exports.useRouterTransition=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-DQ0bWmlq.js`);let t=require(`react`),n=require(`@real-router/core`),r=require(`@real-router/route-utils`),i=require(`react/jsx-runtime`);function a(e){return null}a.displayName=`RouteView.Match`;function o(e){return null}o.displayName=`RouteView.NotFound`;function s(e,t,n){return t===``?!1:n?e===t:(0,r.startsWithSegment)(e,t)}function c(e,n){for(let r of t.Children.toArray(e))(0,t.isValidElement)(r)&&(r.type===a||r.type===o?n.push(r):c(r.props.children,n))}function l(e,n,r,a,o){let s=o===void 0?e:(0,i.jsx)(t.Suspense,{fallback:o,children:e});return r?(0,i.jsx)(t.Activity,{mode:a,children:s},n):(0,i.jsx)(t.Fragment,{children:s},n)}function u(e,r,a,c){let u=null,d=!1,f=[];for(let t of e){if(t.type===o){u=t.props.children;continue}let{segment:e,exact:n=!1,keepAlive:i=!1,fallback:p}=t.props,m=a?`${a}.${e}`:e;!d&&s(r,m,n)?(d=!0,c.add(m),f.push(l(t.props.children,m,i,`visible`,p))):i&&c.has(m)&&f.push(l(t.props.children,m,i,`hidden`,p))}return!d&&r===n.UNKNOWN_ROUTE&&u!==null&&f.push((0,i.jsx)(t.Fragment,{children:u},`__route-view-not-found__`)),{rendered:f,activeMatchFound:d}}function d({nodeName:n,children:r}){let{route:a}=e.c(n),o=(0,t.useRef)(null);o.current??=new Set;let s=(0,t.useMemo)(()=>{let e=[];return c(r,e),e},[r]);if(!a)return null;let{rendered:l}=u(s,a.name,n,o.current);return l.length>0?(0,i.jsx)(i.Fragment,{children:l}):null}d.displayName=`RouteView`;const f=Object.assign(d,{Match:a,NotFound:o});exports.Link=e.s,exports.RouteView=f,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.c,exports.useRouteUtils=e.i,exports.useRouter=e.l,exports.useRouterTransition=e.n;
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Children","Suspense","Activity","Fragment","UNKNOWN_ROUTE","useRouteNode"],"sources":["../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/helpers.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"sourcesContent":["import type { MatchProps, NotFoundProps } from \"./types\";\n\nexport function Match(_props: MatchProps): null {\n return null;\n}\n\nMatch.displayName = \"RouteView.Match\";\n\nexport function NotFound(_props: NotFoundProps): null {\n return null;\n}\n\nNotFound.displayName = \"RouteView.NotFound\";\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Activity, Children, Fragment, Suspense, isValidElement } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\n\nimport type { MatchProps, NotFoundProps } from \"./types\";\nimport type { ReactElement, ReactNode } from \"react\";\n\nfunction isSegmentMatch(\n routeName: string,\n fullSegmentName: string,\n exact: boolean,\n): boolean {\n if (exact) {\n return routeName === fullSegmentName;\n }\n\n return startsWithSegment(routeName, fullSegmentName);\n}\n\nexport function collectElements(\n children: ReactNode,\n result: ReactElement[],\n): void {\n // eslint-disable-next-line @eslint-react/no-children-to-array\n for (const child of Children.toArray(children)) {\n if (!isValidElement(child)) {\n continue;\n }\n\n if (child.type === Match || child.type === NotFound) {\n result.push(child);\n } else {\n collectElements(\n (child.props as { readonly children: ReactNode }).children,\n result,\n );\n }\n }\n}\n\nfunction renderMatchElement(\n matchChildren: ReactNode,\n fullSegmentName: string,\n keepAlive: boolean,\n mode: \"visible\" | \"hidden\",\n fallback?: ReactNode,\n): ReactElement {\n const content =\n fallback === undefined ? (\n matchChildren\n ) : (\n <Suspense fallback={fallback}>{matchChildren}</Suspense>\n );\n\n if (keepAlive) {\n return (\n <Activity mode={mode} key={fullSegmentName}>\n {content}\n </Activity>\n );\n }\n\n return <Fragment key={fullSegmentName}>{content}</Fragment>;\n}\n\nexport function buildRenderList(\n elements: ReactElement[],\n routeName: string,\n nodeName: string,\n hasBeenActivated: Set<string>,\n): { rendered: ReactElement[]; activeMatchFound: boolean } {\n let notFoundChildren: ReactNode = null;\n let activeMatchFound = false;\n const rendered: ReactElement[] = [];\n\n for (const child of elements) {\n if (child.type === NotFound) {\n notFoundChildren = (child.props as NotFoundProps).children;\n continue;\n }\n\n const {\n segment,\n exact = false,\n keepAlive = false,\n fallback,\n } = child.props as MatchProps;\n const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n const isActive =\n !activeMatchFound && isSegmentMatch(routeName, fullSegmentName, exact);\n\n if (isActive) {\n activeMatchFound = true;\n hasBeenActivated.add(fullSegmentName);\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"visible\",\n fallback,\n ),\n );\n } else if (keepAlive && hasBeenActivated.has(fullSegmentName)) {\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"hidden\",\n fallback,\n ),\n );\n }\n }\n\n if (\n !activeMatchFound &&\n routeName === UNKNOWN_ROUTE &&\n notFoundChildren !== null\n ) {\n rendered.push(\n <Fragment key=\"__route-view-not-found__\">{notFoundChildren}</Fragment>,\n );\n }\n\n return { rendered, activeMatchFound };\n}\n","import { useRef } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../../hooks/useRouteNode\";\n\nimport type { RouteViewProps } from \"./types\";\nimport type { ReactElement } from \"react\";\n\nfunction RouteViewRoot({\n nodeName,\n children,\n}: Readonly<RouteViewProps>): ReactElement | null {\n const { route } = useRouteNode(nodeName);\n const hasBeenActivatedRef = useRef<Set<string>>(new Set());\n\n if (!route) {\n return null;\n }\n\n const elements: ReactElement[] = [];\n\n collectElements(children, elements);\n\n const { rendered } = buildRenderList(\n elements,\n route.name,\n nodeName,\n // eslint-disable-next-line @eslint-react/refs -- stable Set ref read for keepAlive tracking (never reassigned)\n hasBeenActivatedRef.current,\n );\n\n if (rendered.length > 0) {\n return <>{rendered}</>;\n }\n\n return null;\n}\n\nRouteViewRoot.displayName = \"RouteView\";\n\nexport const RouteView = Object.assign(RouteViewRoot, { Match, NotFound });\n\nexport type {\n RouteViewProps,\n MatchProps as RouteViewMatchProps,\n NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n"],"mappings":"8OAEA,SAAgB,EAAM,EAA0B,CAC9C,OAAO,KAGT,EAAM,YAAc,kBAEpB,SAAgB,EAAS,EAA6B,CACpD,OAAO,KAGT,EAAS,YAAc,qBCHvB,SAAS,EACP,EACA,EACA,EACS,CAKT,OAJI,EACK,IAAc,GAGvB,EAAA,EAAA,mBAAyB,EAAW,EAAgB,CAGtD,SAAgB,EACd,EACA,EACM,CAEN,IAAK,IAAM,KAASA,EAAAA,SAAS,QAAQ,EAAS,EACxC,EAAA,EAAA,gBAAgB,EAAM,GAItB,EAAM,OAAS,GAAS,EAAM,OAAS,EACzC,EAAO,KAAK,EAAM,CAElB,EACG,EAAM,MAA2C,SAClD,EACD,EAKP,SAAS,EACP,EACA,EACA,EACA,EACA,EACc,CACd,IAAM,EACJ,IAAa,IAAA,GACX,GAEA,EAAA,EAAA,KAACC,EAAAA,SAAD,CAAoB,oBAAW,EAAyB,CAAA,CAW5D,OARI,GAEA,EAAA,EAAA,KAACC,EAAAA,SAAD,CAAgB,gBACb,EACQ,CAFgB,EAEhB,EAIR,EAAA,EAAA,KAACC,EAAAA,SAAD,CAAA,SAAiC,EAAmB,CAArC,EAAqC,CAG7D,SAAgB,EACd,EACA,EACA,EACA,EACyD,CACzD,IAAI,EAA8B,KAC9B,EAAmB,GACjB,EAA2B,EAAE,CAEnC,IAAK,IAAM,KAAS,EAAU,CAC5B,GAAI,EAAM,OAAS,EAAU,CAC3B,EAAoB,EAAM,MAAwB,SAClD,SAGF,GAAM,CACJ,UACA,QAAQ,GACR,YAAY,GACZ,YACE,EAAM,MACJ,EAAkB,EAAW,GAAG,EAAS,GAAG,IAAY,EAE5D,CAAC,GAAoB,EAAe,EAAW,EAAiB,EAAM,EAGtE,EAAmB,GACnB,EAAiB,IAAI,EAAgB,CACrC,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,UACA,EACD,CACF,EACQ,GAAa,EAAiB,IAAI,EAAgB,EAC3D,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,SACA,EACD,CACF,CAcL,MATE,CAAC,GACD,IAAcC,EAAAA,eACd,IAAqB,MAErB,EAAS,MACP,EAAA,EAAA,KAACD,EAAAA,SAAD,CAAA,SAA0C,EAA4B,CAAxD,2BAAwD,CACvE,CAGI,CAAE,WAAU,mBAAkB,CCvHvC,SAAS,EAAc,CACrB,WACA,YACgD,CAChD,GAAM,CAAE,SAAUE,EAAAA,EAAa,EAAS,CAClC,GAAA,EAAA,EAAA,QAA0C,IAAI,IAAM,CAE1D,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAA2B,EAAE,CAEnC,EAAgB,EAAU,EAAS,CAEnC,GAAM,CAAE,YAAa,EACnB,EACA,EAAM,KACN,EAEA,EAAoB,QACrB,CAMD,OAJI,EAAS,OAAS,GACb,EAAA,EAAA,KAAA,EAAA,SAAA,CAAA,SAAG,EAAY,CAAA,CAGjB,KAGT,EAAc,YAAc,YAE5B,MAAa,EAAY,OAAO,OAAO,EAAe,CAAE,QAAO,WAAU,CAAC"}
1
+ {"version":3,"file":"index.js","names":["Children","Suspense","Activity","Fragment","UNKNOWN_ROUTE","useRouteNode"],"sources":["../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/helpers.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"sourcesContent":["import type { MatchProps, NotFoundProps } from \"./types\";\n\nexport function Match(_props: MatchProps): null {\n return null;\n}\n\nMatch.displayName = \"RouteView.Match\";\n\nexport function NotFound(_props: NotFoundProps): null {\n return null;\n}\n\nNotFound.displayName = \"RouteView.NotFound\";\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Activity, Children, Fragment, Suspense, isValidElement } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\n\nimport type { MatchProps, NotFoundProps } from \"./types\";\nimport type { ReactElement, ReactNode } from \"react\";\n\nfunction isSegmentMatch(\n routeName: string,\n fullSegmentName: string,\n exact: boolean,\n): boolean {\n if (fullSegmentName === \"\") {\n return false;\n }\n\n if (exact) {\n return routeName === fullSegmentName;\n }\n\n return startsWithSegment(routeName, fullSegmentName);\n}\n\nexport function collectElements(\n children: ReactNode,\n result: ReactElement[],\n): void {\n // eslint-disable-next-line @eslint-react/no-children-to-array\n for (const child of Children.toArray(children)) {\n if (!isValidElement(child)) {\n continue;\n }\n\n if (child.type === Match || child.type === NotFound) {\n result.push(child);\n } else {\n collectElements(\n (child.props as { readonly children: ReactNode }).children,\n result,\n );\n }\n }\n}\n\nfunction renderMatchElement(\n matchChildren: ReactNode,\n fullSegmentName: string,\n keepAlive: boolean,\n mode: \"visible\" | \"hidden\",\n fallback?: ReactNode,\n): ReactElement {\n const content =\n fallback === undefined ? (\n matchChildren\n ) : (\n <Suspense fallback={fallback}>{matchChildren}</Suspense>\n );\n\n if (keepAlive) {\n return (\n <Activity mode={mode} key={fullSegmentName}>\n {content}\n </Activity>\n );\n }\n\n return <Fragment key={fullSegmentName}>{content}</Fragment>;\n}\n\nexport function buildRenderList(\n elements: ReactElement[],\n routeName: string,\n nodeName: string,\n hasBeenActivated: Set<string>,\n): { rendered: ReactElement[]; activeMatchFound: boolean } {\n let notFoundChildren: ReactNode = null;\n let activeMatchFound = false;\n const rendered: ReactElement[] = [];\n\n for (const child of elements) {\n if (child.type === NotFound) {\n notFoundChildren = (child.props as NotFoundProps).children;\n continue;\n }\n\n const {\n segment,\n exact = false,\n keepAlive = false,\n fallback,\n } = child.props as MatchProps;\n const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n const isActive =\n !activeMatchFound && isSegmentMatch(routeName, fullSegmentName, exact);\n\n if (isActive) {\n activeMatchFound = true;\n hasBeenActivated.add(fullSegmentName);\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"visible\",\n fallback,\n ),\n );\n } else if (keepAlive && hasBeenActivated.has(fullSegmentName)) {\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"hidden\",\n fallback,\n ),\n );\n }\n }\n\n if (\n !activeMatchFound &&\n routeName === UNKNOWN_ROUTE &&\n notFoundChildren !== null\n ) {\n rendered.push(\n <Fragment key=\"__route-view-not-found__\">{notFoundChildren}</Fragment>,\n );\n }\n\n return { rendered, activeMatchFound };\n}\n","import { useMemo, useRef } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../../hooks/useRouteNode\";\n\nimport type { RouteViewProps } from \"./types\";\nimport type { ReactElement } from \"react\";\n\nfunction RouteViewRoot({\n nodeName,\n children,\n}: Readonly<RouteViewProps>): ReactElement | null {\n const { route } = useRouteNode(nodeName);\n const hasBeenActivatedRef = useRef<Set<string> | null>(null);\n\n // eslint-disable-next-line @eslint-react/refs -- lazy init: assign once when null to avoid `new Set()` allocation on every render\n hasBeenActivatedRef.current ??= new Set();\n\n // Skip the Children.toArray + collectElements traversal when children\n // reference is unchanged. children only changes when the parent re-renders\n // with a new ReactNode, so this caches the steady-state.\n const elements = useMemo(() => {\n const collected: ReactElement[] = [];\n\n collectElements(children, collected);\n\n return collected;\n }, [children]);\n\n if (!route) {\n return null;\n }\n\n const { rendered } = buildRenderList(\n elements,\n route.name,\n nodeName,\n // eslint-disable-next-line @eslint-react/refs -- stable Set ref read for keepAlive tracking (never reassigned)\n hasBeenActivatedRef.current,\n );\n\n if (rendered.length > 0) {\n return <>{rendered}</>;\n }\n\n return null;\n}\n\nRouteViewRoot.displayName = \"RouteView\";\n\nexport const RouteView = Object.assign(RouteViewRoot, { Match, NotFound });\n\nexport type {\n RouteViewProps,\n MatchProps as RouteViewMatchProps,\n NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n"],"mappings":"8OAEA,SAAgB,EAAM,EAA0B,CAC9C,OAAO,KAGT,EAAM,YAAc,kBAEpB,SAAgB,EAAS,EAA6B,CACpD,OAAO,KAGT,EAAS,YAAc,qBCHvB,SAAS,EACP,EACA,EACA,EACS,CAST,OARI,IAAoB,GACf,GAGL,EACK,IAAc,GAGvB,EAAA,EAAA,mBAAyB,EAAW,EAAgB,CAGtD,SAAgB,EACd,EACA,EACM,CAEN,IAAK,IAAM,KAASA,EAAAA,SAAS,QAAQ,EAAS,EACxC,EAAA,EAAA,gBAAgB,EAAM,GAItB,EAAM,OAAS,GAAS,EAAM,OAAS,EACzC,EAAO,KAAK,EAAM,CAElB,EACG,EAAM,MAA2C,SAClD,EACD,EAKP,SAAS,EACP,EACA,EACA,EACA,EACA,EACc,CACd,IAAM,EACJ,IAAa,IAAA,GACX,GAEA,EAAA,EAAA,KAACC,EAAAA,SAAD,CAAoB,oBAAW,EAAyB,CAAA,CAW5D,OARI,GAEA,EAAA,EAAA,KAACC,EAAAA,SAAD,CAAgB,gBACb,EACQ,CAFgB,EAEhB,EAIR,EAAA,EAAA,KAACC,EAAAA,SAAD,CAAA,SAAiC,EAAmB,CAArC,EAAqC,CAG7D,SAAgB,EACd,EACA,EACA,EACA,EACyD,CACzD,IAAI,EAA8B,KAC9B,EAAmB,GACjB,EAA2B,EAAE,CAEnC,IAAK,IAAM,KAAS,EAAU,CAC5B,GAAI,EAAM,OAAS,EAAU,CAC3B,EAAoB,EAAM,MAAwB,SAClD,SAGF,GAAM,CACJ,UACA,QAAQ,GACR,YAAY,GACZ,YACE,EAAM,MACJ,EAAkB,EAAW,GAAG,EAAS,GAAG,IAAY,EAE5D,CAAC,GAAoB,EAAe,EAAW,EAAiB,EAAM,EAGtE,EAAmB,GACnB,EAAiB,IAAI,EAAgB,CACrC,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,UACA,EACD,CACF,EACQ,GAAa,EAAiB,IAAI,EAAgB,EAC3D,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,SACA,EACD,CACF,CAcL,MATE,CAAC,GACD,IAAcC,EAAAA,eACd,IAAqB,MAErB,EAAS,MACP,EAAA,EAAA,KAACD,EAAAA,SAAD,CAAA,SAA0C,EAA4B,CAAxD,2BAAwD,CACvE,CAGI,CAAE,WAAU,mBAAkB,CC3HvC,SAAS,EAAc,CACrB,WACA,YACgD,CAChD,GAAM,CAAE,SAAUE,EAAAA,EAAa,EAAS,CAClC,GAAA,EAAA,EAAA,QAAiD,KAAK,CAG5D,EAAoB,UAAY,IAAI,IAKpC,IAAM,GAAA,EAAA,EAAA,aAAyB,CAC7B,IAAM,EAA4B,EAAE,CAIpC,OAFA,EAAgB,EAAU,EAAU,CAE7B,GACN,CAAC,EAAS,CAAC,CAEd,GAAI,CAAC,EACH,OAAO,KAGT,GAAM,CAAE,YAAa,EACnB,EACA,EAAM,KACN,EAEA,EAAoB,QACrB,CAMD,OAJI,EAAS,OAAS,GACb,EAAA,EAAA,KAAA,EAAA,SAAA,CAAA,SAAG,EAAY,CAAA,CAGjB,KAGT,EAAc,YAAc,YAE5B,MAAa,EAAY,OAAO,OAAO,EAAe,CAAE,QAAO,WAAU,CAAC"}
@@ -1,4 +1,4 @@
1
- import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-GwajvaK9.js";
1
+ import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-Ddv8Rs1U.js";
2
2
  import { Navigator } from "@real-router/core";
3
3
  import { RouterTransitionSnapshot } from "@real-router/sources";
4
4
  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-jqJCKUhp.js`);exports.Link=e.s,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.c,exports.useRouteUtils=e.i,exports.useRouter=e.l,exports.useRouterTransition=e.n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./RouterProvider-DQ0bWmlq.js`);exports.Link=e.s,exports.RouterErrorBoundary=e.o,exports.RouterProvider=e.t,exports.useNavigator=e.a,exports.useRoute=e.r,exports.useRouteNode=e.c,exports.useRouteUtils=e.i,exports.useRouter=e.l,exports.useRouterTransition=e.n;
@@ -48,8 +48,9 @@ declare const useNavigator: () => Navigator;
48
48
  /**
49
49
  * Returns a pre-computed {@link RouteUtils} instance for the current router.
50
50
  *
51
- * Internally retrieves the route tree via `getPluginApi` and delegates
52
- * to `getRouteUtils`, which caches instances per tree reference (WeakMap).
51
+ * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside
52
+ * `@real-router/route-utils`, so the same router always returns the same
53
+ * `RouteUtils` instance across renders — no local cache needed in the adapter.
53
54
  *
54
55
  * @returns RouteUtils instance with pre-computed chains and siblings
55
56
  *
@@ -87,4 +88,4 @@ interface RouteProviderProps {
87
88
  declare const RouterProvider: FC<RouteProviderProps>;
88
89
  //#endregion
89
90
  export { useRouteUtils as a, RouterErrorBoundary as c, LinkProps as d, useRoute as i, RouterErrorBoundaryProps as l, useRouterTransition as n, useNavigator as o, useRouteNode as r, useRouter as s, RouterProvider as t, Link as u };
90
- //# sourceMappingURL=RouterProvider-BuGQIoZ-.d.mts.map
91
+ //# sourceMappingURL=RouterProvider-B2KCZfL_.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterProvider-BuGQIoZ-.d.mts","names":[],"sources":["../../src/types.ts","../../src/components/Link.tsx","../../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","../../src/RouterProvider.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA;EACV,SAAA,EAAW,SAAA;AAAA,IACT,UAAA;AAAA,UAEa,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;;;cCMrB,IAAA,EAAM,EAAA,CAAG,SAAA;;;UC3BL,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;;;cCbrB,SAAA,QAAgB,MAAA;;;cCAhB,YAAA,QAAmB,SAAA;;;;;;;;AJAhC;;;;;;;;;;;;;;;;;cKuBa,aAAA,QAAoB,UAAA;;;cCvBpB,QAAA,QAAe,YAAA;;;iBCAZ,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA;;;UCGtB,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
1
+ {"version":3,"file":"RouterProvider-B2KCZfL_.d.mts","names":[],"sources":["../../src/types.ts","../../src/components/Link.tsx","../../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","../../src/RouterProvider.tsx"],"mappings":";;;;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA;EACV,SAAA,EAAW,SAAA;AAAA,IACT,UAAA;AAAA,UAEa,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;;;cCuErB,IAAA,EAAM,EAAA,CAAG,SAAA;;;UC3FL,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;;;;;;;;AJAhC;;;;;;;;;;;;;;;;;;cKsBa,aAAA,QAAoB,UAAA;;;cCtBpB,QAAA,QAAe,YAAA;;;iBCAZ,YAAA,CAAa,QAAA,WAAmB,YAAA;;;iBCDhC,mBAAA,CAAA,GAAuB,wBAAA;;;UCGtB,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,QAAA,EAAU,SAAA;EACV,kBAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,EAAA,CAAG,kBAAA"}
@@ -0,0 +1,2 @@
1
+ import{createContext as e,memo as t,useCallback as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useSyncExternalStore as s}from"react";import{getNavigator as c}from"@real-router/core";import{getRouteUtils as l}from"@real-router/route-utils";import{Fragment as u,jsx as d,jsxs as f}from"react/jsx-runtime";import{createActiveRouteSource as p,createDismissableError as m,createRouteNodeSource as h,createRouteSource as g,getTransitionSource as _}from"@real-router/sources";import{getPluginApi as v}from"@real-router/core/api";const y=e(null),b=e(null),x=e(null),S=()=>{let e=r(b);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e};function C(e){let t=S(),n=a(()=>h(t,e),[t,e]),r=s(n.subscribe,n.getSnapshot,n.getSnapshot),i=c(t);return a(()=>({navigator:i,route:r.route,previousRoute:r.previousRoute}),[i,r])}const w=Object.freeze({}),T=Object.freeze({}),E=`data-real-router-announcer`;function D(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,u=O(),d=(e,t)=>{s=e,clearTimeout(l),u.textContent=e,l=setTimeout(()=>{u.textContent=``,s=``},7e3),j(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=A(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),k()}}}function O(){let e=document.querySelector(`[${E}]`);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(E,``),document.body.prepend(t),t}function k(){document.querySelector(`[${E}]`)?.remove()}function A(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 j(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}function M(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function N(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 P(e){return e?e.match(/\S+/g)??[]:[]}function F(e,t,n){if(e&&t){let e=P(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=P(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 I(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 L(e,t,n=!1,r=!0){let i=p(S(),e,t,{strict:n,ignoreQueryParams:r});return s(i.subscribe,i.getSnapshot,i.getSnapshot)}function R(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&&I(e.routeParams,t.routeParams)&&I(e.routeOptions,t.routeOptions)}const z=t(({routeName:e,routeParams:t=w,routeOptions:r=T,className:i,activeClassName:o=`active`,activeStrict:s=!1,ignoreQueryParams:c=!0,onClick:l,target:u,children:f,...p})=>{let m=S(),h=L(e,t,s,c),g=a(()=>N(m,e,t),[m,e,t]),_=n(n=>{l&&(l(n),n.defaultPrevented)||!M(n.nativeEvent)||u===`_blank`||(n.preventDefault(),m.navigate(e,t,r).catch(()=>{}))},[l,u,m,e,t,r]),v=F(h,o,i);return d(`a`,{...p,href:g,className:v,onClick:_,children:f})},R);z.displayName=`Link`;function B({children:e,fallback:t,onError:n}){let r=m(S()),a=s(r.subscribe,r.getSnapshot,r.getSnapshot),c=o(n);return c.current=n,i(()=>{a.error&&c.current?.(a.error,a.toRoute,a.fromRoute)},[a.version]),f(u,{children:[e,a.error?t(a.error,a.resetError):null]})}const V=()=>{let e=r(x);if(!e)throw Error(`useNavigator must be used within a RouterProvider`);return e},H=()=>l(v(S()).getTree()),U=()=>{let e=r(y);if(!e)throw Error(`useRoute must be used within a RouteProvider`);return e};function W(){let e=_(S());return s(e.subscribe,e.getSnapshot,e.getSnapshot)}const G=({router:e,children:t,announceNavigation:n})=>{i(()=>{if(!n)return;let t=D(e);return()=>{t.destroy()}},[n,e]);let r=a(()=>c(e),[e]),o=a(()=>g(e),[e]),l=s(o.subscribe,o.getSnapshot,o.getSnapshot),u=a(()=>({navigator:r,route:l.route,previousRoute:l.previousRoute}),[r,l]);return d(b.Provider,{value:e,children:d(x.Provider,{value:r,children:d(y.Provider,{value:u,children:t})})})};export{V as a,C as c,H as i,S as l,W as n,B as o,U as r,z as s,G as t};
2
+ //# sourceMappingURL=RouterProvider-DqCTMwvh.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterProvider-DqCTMwvh.mjs","names":[],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/Link.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, 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 { 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/index.js\";\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","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\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\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 { createRouteAnnouncer } from \"./dom-utils/index.js\";\n\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}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\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 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,4 +1,4 @@
1
- import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-BuGQIoZ-.mjs";
1
+ import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-B2KCZfL_.mjs";
2
2
  import { ReactElement, ReactNode } from "react";
3
3
  import { Navigator } from "@real-router/core";
4
4
  import { RouterTransitionSnapshot } from "@real-router/sources";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/components/modern/RouteView/types.ts","../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"mappings":";;;;;;UAEiB,cAAA;;WAEN,QAAA;;WAEA,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,UAAA;;WAEN,OAAA;EAPA;EAAA,SASA,KAAA;EAPU;EAAA,SASV,SAAA;EATmB;EAAA,SAWnB,QAAA,GAAW,SAAA;EARK;EAAA,SAUhB,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,aAAA;EATN;EAAA,SAWA,QAAA,EAAU,SAAA;AAAA;;;iBCtBL,KAAA,CAAM,MAAA,EAAQ,UAAA;AAAA,kBAAd,KAAA;EAAA,IAAK,WAAA;AAAA;AAAA,iBAML,QAAA,CAAS,MAAA,EAAQ,aAAA;AAAA,kBAAjB,QAAA;EAAA,IAAQ,WAAA;AAAA;;;iBCCf,aAAA,CAAA;EACP,QAAA;EACA;AAAA,GACC,QAAA,CAAS,cAAA,IAAkB,YAAA;AAAA,kBAHrB,aAAA;EAAA,IAAa,WAAA;AAAA;AAAA,cAgCT,SAAA,SAAS,aAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/components/modern/RouteView/types.ts","../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"mappings":";;;;;;UAEiB,cAAA;;WAEN,QAAA;;WAEA,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,UAAA;;WAEN,OAAA;EAPA;EAAA,SASA,KAAA;EAPU;EAAA,SASV,SAAA;EATmB;EAAA,SAWnB,QAAA,GAAW,SAAA;EARK;EAAA,SAUhB,QAAA,EAAU,SAAA;AAAA;AAAA,UAGJ,aAAA;EATN;EAAA,SAWA,QAAA,EAAU,SAAA;AAAA;;;iBCtBL,KAAA,CAAM,MAAA,EAAQ,UAAA;AAAA,kBAAd,KAAA;EAAA,IAAK,WAAA;AAAA;AAAA,iBAML,QAAA,CAAS,MAAA,EAAQ,aAAA;AAAA,kBAAjB,QAAA;EAAA,IAAQ,WAAA;AAAA;;;iBCCf,aAAA,CAAA;EACP,QAAA;EACA;AAAA,GACC,QAAA,CAAS,cAAA,IAAkB,YAAA;AAAA,kBAHrB,aAAA;EAAA,IAAa,WAAA;AAAA;AAAA,cA0CT,SAAA,SAAS,aAAA"}
@@ -1,2 +1,2 @@
1
- import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"./RouterProvider-DrAGH6K9.mjs";import{Activity as l,Children as u,Fragment as d,Suspense as f,isValidElement as p,useRef as m}from"react";import{UNKNOWN_ROUTE as h}from"@real-router/core";import{startsWithSegment as g}from"@real-router/route-utils";import{Fragment as _,jsx as v}from"react/jsx-runtime";function y(e){return null}y.displayName=`RouteView.Match`;function b(e){return null}b.displayName=`RouteView.NotFound`;function x(e,t,n){return n?e===t:g(e,t)}function S(e,t){for(let n of u.toArray(e))p(n)&&(n.type===y||n.type===b?t.push(n):S(n.props.children,t))}function C(e,t,n,r,i){let a=i===void 0?e:v(f,{fallback:i,children:e});return n?v(l,{mode:r,children:a},t):v(d,{children:a},t)}function w(e,t,n,r){let i=null,a=!1,o=[];for(let s of e){if(s.type===b){i=s.props.children;continue}let{segment:e,exact:c=!1,keepAlive:l=!1,fallback:u}=s.props,d=n?`${n}.${e}`:e;!a&&x(t,d,c)?(a=!0,r.add(d),o.push(C(s.props.children,d,l,`visible`,u))):l&&r.has(d)&&o.push(C(s.props.children,d,l,`hidden`,u))}return!a&&t===h&&i!==null&&o.push(v(d,{children:i},`__route-view-not-found__`)),{rendered:o,activeMatchFound:a}}function T({nodeName:e,children:n}){let{route:r}=t(e),i=m(new Set);if(!r)return null;let a=[];S(n,a);let{rendered:o}=w(a,r.name,e,i.current);return o.length>0?v(_,{children:o}):null}T.displayName=`RouteView`;const E=Object.assign(T,{Match:y,NotFound:b});export{s as Link,E as RouteView,a as RouterErrorBoundary,c as RouterProvider,e as useNavigator,o as useRoute,t as useRouteNode,n as useRouteUtils,r as useRouter,i as useRouterTransition};
1
+ import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"./RouterProvider-DqCTMwvh.mjs";import{Activity as l,Children as u,Fragment as d,Suspense as f,isValidElement as p,useMemo as m,useRef as h}from"react";import{UNKNOWN_ROUTE as g}from"@real-router/core";import{startsWithSegment as _}from"@real-router/route-utils";import{Fragment as v,jsx as y}from"react/jsx-runtime";function b(e){return null}b.displayName=`RouteView.Match`;function x(e){return null}x.displayName=`RouteView.NotFound`;function S(e,t,n){return t===``?!1:n?e===t:_(e,t)}function C(e,t){for(let n of u.toArray(e))p(n)&&(n.type===b||n.type===x?t.push(n):C(n.props.children,t))}function w(e,t,n,r,i){let a=i===void 0?e:y(f,{fallback:i,children:e});return n?y(l,{mode:r,children:a},t):y(d,{children:a},t)}function T(e,t,n,r){let i=null,a=!1,o=[];for(let s of e){if(s.type===x){i=s.props.children;continue}let{segment:e,exact:c=!1,keepAlive:l=!1,fallback:u}=s.props,d=n?`${n}.${e}`:e;!a&&S(t,d,c)?(a=!0,r.add(d),o.push(w(s.props.children,d,l,`visible`,u))):l&&r.has(d)&&o.push(w(s.props.children,d,l,`hidden`,u))}return!a&&t===g&&i!==null&&o.push(y(d,{children:i},`__route-view-not-found__`)),{rendered:o,activeMatchFound:a}}function E({nodeName:e,children:n}){let{route:r}=t(e),i=h(null);i.current??=new Set;let a=m(()=>{let e=[];return C(n,e),e},[n]);if(!r)return null;let{rendered:o}=T(a,r.name,e,i.current);return o.length>0?y(v,{children:o}):null}E.displayName=`RouteView`;const D=Object.assign(E,{Match:b,NotFound:x});export{s as Link,D as RouteView,a as RouterErrorBoundary,c as RouterProvider,e as useNavigator,o as useRoute,t as useRouteNode,n as useRouteUtils,r as useRouter,i as useRouterTransition};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/helpers.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"sourcesContent":["import type { MatchProps, NotFoundProps } from \"./types\";\n\nexport function Match(_props: MatchProps): null {\n return null;\n}\n\nMatch.displayName = \"RouteView.Match\";\n\nexport function NotFound(_props: NotFoundProps): null {\n return null;\n}\n\nNotFound.displayName = \"RouteView.NotFound\";\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Activity, Children, Fragment, Suspense, isValidElement } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\n\nimport type { MatchProps, NotFoundProps } from \"./types\";\nimport type { ReactElement, ReactNode } from \"react\";\n\nfunction isSegmentMatch(\n routeName: string,\n fullSegmentName: string,\n exact: boolean,\n): boolean {\n if (exact) {\n return routeName === fullSegmentName;\n }\n\n return startsWithSegment(routeName, fullSegmentName);\n}\n\nexport function collectElements(\n children: ReactNode,\n result: ReactElement[],\n): void {\n // eslint-disable-next-line @eslint-react/no-children-to-array\n for (const child of Children.toArray(children)) {\n if (!isValidElement(child)) {\n continue;\n }\n\n if (child.type === Match || child.type === NotFound) {\n result.push(child);\n } else {\n collectElements(\n (child.props as { readonly children: ReactNode }).children,\n result,\n );\n }\n }\n}\n\nfunction renderMatchElement(\n matchChildren: ReactNode,\n fullSegmentName: string,\n keepAlive: boolean,\n mode: \"visible\" | \"hidden\",\n fallback?: ReactNode,\n): ReactElement {\n const content =\n fallback === undefined ? (\n matchChildren\n ) : (\n <Suspense fallback={fallback}>{matchChildren}</Suspense>\n );\n\n if (keepAlive) {\n return (\n <Activity mode={mode} key={fullSegmentName}>\n {content}\n </Activity>\n );\n }\n\n return <Fragment key={fullSegmentName}>{content}</Fragment>;\n}\n\nexport function buildRenderList(\n elements: ReactElement[],\n routeName: string,\n nodeName: string,\n hasBeenActivated: Set<string>,\n): { rendered: ReactElement[]; activeMatchFound: boolean } {\n let notFoundChildren: ReactNode = null;\n let activeMatchFound = false;\n const rendered: ReactElement[] = [];\n\n for (const child of elements) {\n if (child.type === NotFound) {\n notFoundChildren = (child.props as NotFoundProps).children;\n continue;\n }\n\n const {\n segment,\n exact = false,\n keepAlive = false,\n fallback,\n } = child.props as MatchProps;\n const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n const isActive =\n !activeMatchFound && isSegmentMatch(routeName, fullSegmentName, exact);\n\n if (isActive) {\n activeMatchFound = true;\n hasBeenActivated.add(fullSegmentName);\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"visible\",\n fallback,\n ),\n );\n } else if (keepAlive && hasBeenActivated.has(fullSegmentName)) {\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"hidden\",\n fallback,\n ),\n );\n }\n }\n\n if (\n !activeMatchFound &&\n routeName === UNKNOWN_ROUTE &&\n notFoundChildren !== null\n ) {\n rendered.push(\n <Fragment key=\"__route-view-not-found__\">{notFoundChildren}</Fragment>,\n );\n }\n\n return { rendered, activeMatchFound };\n}\n","import { useRef } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../../hooks/useRouteNode\";\n\nimport type { RouteViewProps } from \"./types\";\nimport type { ReactElement } from \"react\";\n\nfunction RouteViewRoot({\n nodeName,\n children,\n}: Readonly<RouteViewProps>): ReactElement | null {\n const { route } = useRouteNode(nodeName);\n const hasBeenActivatedRef = useRef<Set<string>>(new Set());\n\n if (!route) {\n return null;\n }\n\n const elements: ReactElement[] = [];\n\n collectElements(children, elements);\n\n const { rendered } = buildRenderList(\n elements,\n route.name,\n nodeName,\n // eslint-disable-next-line @eslint-react/refs -- stable Set ref read for keepAlive tracking (never reassigned)\n hasBeenActivatedRef.current,\n );\n\n if (rendered.length > 0) {\n return <>{rendered}</>;\n }\n\n return null;\n}\n\nRouteViewRoot.displayName = \"RouteView\";\n\nexport const RouteView = Object.assign(RouteViewRoot, { Match, NotFound });\n\nexport type {\n RouteViewProps,\n MatchProps as RouteViewMatchProps,\n NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n"],"mappings":"qXAEA,SAAgB,EAAM,EAA0B,CAC9C,OAAO,KAGT,EAAM,YAAc,kBAEpB,SAAgB,EAAS,EAA6B,CACpD,OAAO,KAGT,EAAS,YAAc,qBCHvB,SAAS,EACP,EACA,EACA,EACS,CAKT,OAJI,EACK,IAAc,EAGhB,EAAkB,EAAW,EAAgB,CAGtD,SAAgB,EACd,EACA,EACM,CAEN,IAAK,IAAM,KAAS,EAAS,QAAQ,EAAS,CACvC,EAAe,EAAM,GAItB,EAAM,OAAS,GAAS,EAAM,OAAS,EACzC,EAAO,KAAK,EAAM,CAElB,EACG,EAAM,MAA2C,SAClD,EACD,EAKP,SAAS,EACP,EACA,EACA,EACA,EACA,EACc,CACd,IAAM,EACJ,IAAa,IAAA,GACX,EAEA,EAAC,EAAD,CAAoB,oBAAW,EAAyB,CAAA,CAW5D,OARI,EAEA,EAAC,EAAD,CAAgB,gBACb,EACQ,CAFgB,EAEhB,CAIR,EAAC,EAAD,CAAA,SAAiC,EAAmB,CAArC,EAAqC,CAG7D,SAAgB,EACd,EACA,EACA,EACA,EACyD,CACzD,IAAI,EAA8B,KAC9B,EAAmB,GACjB,EAA2B,EAAE,CAEnC,IAAK,IAAM,KAAS,EAAU,CAC5B,GAAI,EAAM,OAAS,EAAU,CAC3B,EAAoB,EAAM,MAAwB,SAClD,SAGF,GAAM,CACJ,UACA,QAAQ,GACR,YAAY,GACZ,YACE,EAAM,MACJ,EAAkB,EAAW,GAAG,EAAS,GAAG,IAAY,EAE5D,CAAC,GAAoB,EAAe,EAAW,EAAiB,EAAM,EAGtE,EAAmB,GACnB,EAAiB,IAAI,EAAgB,CACrC,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,UACA,EACD,CACF,EACQ,GAAa,EAAiB,IAAI,EAAgB,EAC3D,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,SACA,EACD,CACF,CAcL,MATE,CAAC,GACD,IAAc,GACd,IAAqB,MAErB,EAAS,KACP,EAAC,EAAD,CAAA,SAA0C,EAA4B,CAAxD,2BAAwD,CACvE,CAGI,CAAE,WAAU,mBAAkB,CCvHvC,SAAS,EAAc,CACrB,WACA,YACgD,CAChD,GAAM,CAAE,SAAU,EAAa,EAAS,CAClC,EAAsB,EAAoB,IAAI,IAAM,CAE1D,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAA2B,EAAE,CAEnC,EAAgB,EAAU,EAAS,CAEnC,GAAM,CAAE,YAAa,EACnB,EACA,EAAM,KACN,EAEA,EAAoB,QACrB,CAMD,OAJI,EAAS,OAAS,EACb,EAAA,EAAA,CAAA,SAAG,EAAY,CAAA,CAGjB,KAGT,EAAc,YAAc,YAE5B,MAAa,EAAY,OAAO,OAAO,EAAe,CAAE,QAAO,WAAU,CAAC"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/helpers.tsx","../../src/components/modern/RouteView/RouteView.tsx"],"sourcesContent":["import type { MatchProps, NotFoundProps } from \"./types\";\n\nexport function Match(_props: MatchProps): null {\n return null;\n}\n\nMatch.displayName = \"RouteView.Match\";\n\nexport function NotFound(_props: NotFoundProps): null {\n return null;\n}\n\nNotFound.displayName = \"RouteView.NotFound\";\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Activity, Children, Fragment, Suspense, isValidElement } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\n\nimport type { MatchProps, NotFoundProps } from \"./types\";\nimport type { ReactElement, ReactNode } from \"react\";\n\nfunction isSegmentMatch(\n routeName: string,\n fullSegmentName: string,\n exact: boolean,\n): boolean {\n if (fullSegmentName === \"\") {\n return false;\n }\n\n if (exact) {\n return routeName === fullSegmentName;\n }\n\n return startsWithSegment(routeName, fullSegmentName);\n}\n\nexport function collectElements(\n children: ReactNode,\n result: ReactElement[],\n): void {\n // eslint-disable-next-line @eslint-react/no-children-to-array\n for (const child of Children.toArray(children)) {\n if (!isValidElement(child)) {\n continue;\n }\n\n if (child.type === Match || child.type === NotFound) {\n result.push(child);\n } else {\n collectElements(\n (child.props as { readonly children: ReactNode }).children,\n result,\n );\n }\n }\n}\n\nfunction renderMatchElement(\n matchChildren: ReactNode,\n fullSegmentName: string,\n keepAlive: boolean,\n mode: \"visible\" | \"hidden\",\n fallback?: ReactNode,\n): ReactElement {\n const content =\n fallback === undefined ? (\n matchChildren\n ) : (\n <Suspense fallback={fallback}>{matchChildren}</Suspense>\n );\n\n if (keepAlive) {\n return (\n <Activity mode={mode} key={fullSegmentName}>\n {content}\n </Activity>\n );\n }\n\n return <Fragment key={fullSegmentName}>{content}</Fragment>;\n}\n\nexport function buildRenderList(\n elements: ReactElement[],\n routeName: string,\n nodeName: string,\n hasBeenActivated: Set<string>,\n): { rendered: ReactElement[]; activeMatchFound: boolean } {\n let notFoundChildren: ReactNode = null;\n let activeMatchFound = false;\n const rendered: ReactElement[] = [];\n\n for (const child of elements) {\n if (child.type === NotFound) {\n notFoundChildren = (child.props as NotFoundProps).children;\n continue;\n }\n\n const {\n segment,\n exact = false,\n keepAlive = false,\n fallback,\n } = child.props as MatchProps;\n const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n const isActive =\n !activeMatchFound && isSegmentMatch(routeName, fullSegmentName, exact);\n\n if (isActive) {\n activeMatchFound = true;\n hasBeenActivated.add(fullSegmentName);\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"visible\",\n fallback,\n ),\n );\n } else if (keepAlive && hasBeenActivated.has(fullSegmentName)) {\n rendered.push(\n renderMatchElement(\n (child.props as MatchProps).children,\n fullSegmentName,\n keepAlive,\n \"hidden\",\n fallback,\n ),\n );\n }\n }\n\n if (\n !activeMatchFound &&\n routeName === UNKNOWN_ROUTE &&\n notFoundChildren !== null\n ) {\n rendered.push(\n <Fragment key=\"__route-view-not-found__\">{notFoundChildren}</Fragment>,\n );\n }\n\n return { rendered, activeMatchFound };\n}\n","import { useMemo, useRef } from \"react\";\n\nimport { Match, NotFound } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../../hooks/useRouteNode\";\n\nimport type { RouteViewProps } from \"./types\";\nimport type { ReactElement } from \"react\";\n\nfunction RouteViewRoot({\n nodeName,\n children,\n}: Readonly<RouteViewProps>): ReactElement | null {\n const { route } = useRouteNode(nodeName);\n const hasBeenActivatedRef = useRef<Set<string> | null>(null);\n\n // eslint-disable-next-line @eslint-react/refs -- lazy init: assign once when null to avoid `new Set()` allocation on every render\n hasBeenActivatedRef.current ??= new Set();\n\n // Skip the Children.toArray + collectElements traversal when children\n // reference is unchanged. children only changes when the parent re-renders\n // with a new ReactNode, so this caches the steady-state.\n const elements = useMemo(() => {\n const collected: ReactElement[] = [];\n\n collectElements(children, collected);\n\n return collected;\n }, [children]);\n\n if (!route) {\n return null;\n }\n\n const { rendered } = buildRenderList(\n elements,\n route.name,\n nodeName,\n // eslint-disable-next-line @eslint-react/refs -- stable Set ref read for keepAlive tracking (never reassigned)\n hasBeenActivatedRef.current,\n );\n\n if (rendered.length > 0) {\n return <>{rendered}</>;\n }\n\n return null;\n}\n\nRouteViewRoot.displayName = \"RouteView\";\n\nexport const RouteView = Object.assign(RouteViewRoot, { Match, NotFound });\n\nexport type {\n RouteViewProps,\n MatchProps as RouteViewMatchProps,\n NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n"],"mappings":"kYAEA,SAAgB,EAAM,EAA0B,CAC9C,OAAO,KAGT,EAAM,YAAc,kBAEpB,SAAgB,EAAS,EAA6B,CACpD,OAAO,KAGT,EAAS,YAAc,qBCHvB,SAAS,EACP,EACA,EACA,EACS,CAST,OARI,IAAoB,GACf,GAGL,EACK,IAAc,EAGhB,EAAkB,EAAW,EAAgB,CAGtD,SAAgB,EACd,EACA,EACM,CAEN,IAAK,IAAM,KAAS,EAAS,QAAQ,EAAS,CACvC,EAAe,EAAM,GAItB,EAAM,OAAS,GAAS,EAAM,OAAS,EACzC,EAAO,KAAK,EAAM,CAElB,EACG,EAAM,MAA2C,SAClD,EACD,EAKP,SAAS,EACP,EACA,EACA,EACA,EACA,EACc,CACd,IAAM,EACJ,IAAa,IAAA,GACX,EAEA,EAAC,EAAD,CAAoB,oBAAW,EAAyB,CAAA,CAW5D,OARI,EAEA,EAAC,EAAD,CAAgB,gBACb,EACQ,CAFgB,EAEhB,CAIR,EAAC,EAAD,CAAA,SAAiC,EAAmB,CAArC,EAAqC,CAG7D,SAAgB,EACd,EACA,EACA,EACA,EACyD,CACzD,IAAI,EAA8B,KAC9B,EAAmB,GACjB,EAA2B,EAAE,CAEnC,IAAK,IAAM,KAAS,EAAU,CAC5B,GAAI,EAAM,OAAS,EAAU,CAC3B,EAAoB,EAAM,MAAwB,SAClD,SAGF,GAAM,CACJ,UACA,QAAQ,GACR,YAAY,GACZ,YACE,EAAM,MACJ,EAAkB,EAAW,GAAG,EAAS,GAAG,IAAY,EAE5D,CAAC,GAAoB,EAAe,EAAW,EAAiB,EAAM,EAGtE,EAAmB,GACnB,EAAiB,IAAI,EAAgB,CACrC,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,UACA,EACD,CACF,EACQ,GAAa,EAAiB,IAAI,EAAgB,EAC3D,EAAS,KACP,EACG,EAAM,MAAqB,SAC5B,EACA,EACA,SACA,EACD,CACF,CAcL,MATE,CAAC,GACD,IAAc,GACd,IAAqB,MAErB,EAAS,KACP,EAAC,EAAD,CAAA,SAA0C,EAA4B,CAAxD,2BAAwD,CACvE,CAGI,CAAE,WAAU,mBAAkB,CC3HvC,SAAS,EAAc,CACrB,WACA,YACgD,CAChD,GAAM,CAAE,SAAU,EAAa,EAAS,CAClC,EAAsB,EAA2B,KAAK,CAG5D,EAAoB,UAAY,IAAI,IAKpC,IAAM,EAAW,MAAc,CAC7B,IAAM,EAA4B,EAAE,CAIpC,OAFA,EAAgB,EAAU,EAAU,CAE7B,GACN,CAAC,EAAS,CAAC,CAEd,GAAI,CAAC,EACH,OAAO,KAGT,GAAM,CAAE,YAAa,EACnB,EACA,EAAM,KACN,EAEA,EAAoB,QACrB,CAMD,OAJI,EAAS,OAAS,EACb,EAAA,EAAA,CAAA,SAAG,EAAY,CAAA,CAGjB,KAGT,EAAc,YAAc,YAE5B,MAAa,EAAY,OAAO,OAAO,EAAe,CAAE,QAAO,WAAU,CAAC"}
@@ -1,4 +1,4 @@
1
- import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-BuGQIoZ-.mjs";
1
+ import { a as useRouteUtils, c as RouterErrorBoundary, d as LinkProps, i as useRoute, l as RouterErrorBoundaryProps, n as useRouterTransition, o as useNavigator, r as useRouteNode, s as useRouter, t as RouterProvider, u as Link } from "./RouterProvider-B2KCZfL_.mjs";
2
2
  import { Navigator } from "@real-router/core";
3
3
  import { RouterTransitionSnapshot } from "@real-router/sources";
4
4
  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,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"./RouterProvider-DrAGH6K9.mjs";export{s as Link,a as RouterErrorBoundary,c as RouterProvider,e as useNavigator,o as useRoute,t as useRouteNode,n as useRouteUtils,r as useRouter,i as useRouterTransition};
1
+ import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"./RouterProvider-DqCTMwvh.mjs";export{s as Link,a as RouterErrorBoundary,c as RouterProvider,e as useNavigator,o as useRoute,t as useRouteNode,n as useRouteUtils,r as useRouter,i as useRouterTransition};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/react",
3
- "version": "0.15.1",
3
+ "version": "0.16.1",
4
4
  "type": "commonjs",
5
5
  "description": "React integration for Real-Router",
6
6
  "main": "./dist/cjs/index.js",
@@ -66,7 +66,7 @@
66
66
  "dependencies": {
67
67
  "@real-router/core": "^0.48.0",
68
68
  "@real-router/route-utils": "^0.2.1",
69
- "@real-router/sources": "^0.5.1"
69
+ "@real-router/sources": "^0.6.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@testing-library/dom": "10.4.1",
@@ -74,7 +74,7 @@
74
74
  "@testing-library/react": "16.3.2",
75
75
  "@testing-library/user-event": "14.6.1",
76
76
  "vitest-react-profiler": "1.12.0",
77
- "@real-router/browser-plugin": "^0.12.1"
77
+ "@real-router/browser-plugin": "^0.13.0"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "@types/react": ">=18.0.0",
@@ -83,13 +83,13 @@
83
83
  "react-dom": ">=18.0.0"
84
84
  },
85
85
  "scripts": {
86
- "build": "tsdown --config-loader unrun",
87
86
  "test": "vitest",
88
87
  "test:properties": "vitest run --config vitest.config.properties.mts",
89
88
  "test:stress": "vitest run --config vitest.config.stress.mts",
90
89
  "type-check": "tsc --noEmit",
91
90
  "lint": "eslint --cache --ext .ts,.tsx src/ tests/ --fix --max-warnings 0",
92
91
  "lint:package": "publint",
93
- "lint:types": "attw --pack ."
92
+ "lint:types": "attw --pack .",
93
+ "bundle": "tsdown --config-loader unrun"
94
94
  }
95
95
  }
@@ -37,22 +37,32 @@ export const RouterProvider: FC<RouteProviderProps> = ({
37
37
  // subscribe connects to router on first listener, unsubscribes on last.
38
38
  // This is Strict Mode safe — no useEffect cleanup needed.
39
39
  const store = useMemo(() => createRouteSource(router), [router]);
40
- const { route, previousRoute } = useSyncExternalStore(
40
+ // Use snapshot reference directly. createRouteSource via stabilizeState
41
+ // returns the SAME snapshot reference when route.path is unchanged, so
42
+ // useMemo below sees stable deps for idempotent navigations and
43
+ // RouteContext consumers do not re-render.
44
+ const snapshot = useSyncExternalStore(
41
45
  store.subscribe,
42
46
  store.getSnapshot,
43
47
  store.getSnapshot, // SSR: router returns same state on server and client
44
48
  );
45
49
 
46
50
  const routeContextValue = useMemo(
47
- () => ({ navigator, route, previousRoute }),
48
- [navigator, route, previousRoute],
51
+ () => ({
52
+ navigator,
53
+ route: snapshot.route,
54
+ previousRoute: snapshot.previousRoute,
55
+ }),
56
+ [navigator, snapshot],
49
57
  );
50
58
 
51
59
  return (
52
- <RouterContext value={router}>
53
- <NavigatorContext value={navigator}>
54
- <RouteContext value={routeContextValue}>{children}</RouteContext>
55
- </NavigatorContext>
56
- </RouterContext>
60
+ <RouterContext.Provider value={router}>
61
+ <NavigatorContext.Provider value={navigator}>
62
+ <RouteContext.Provider value={routeContextValue}>
63
+ {children}
64
+ </RouteContext.Provider>
65
+ </NavigatorContext.Provider>
66
+ </RouterContext.Provider>
57
67
  );
58
68
  };
@@ -5,10 +5,10 @@ import {
5
5
  shouldNavigate,
6
6
  buildHref,
7
7
  buildActiveClassName,
8
+ shallowEqual,
8
9
  } from "../dom-utils/index.js";
9
10
  import { useIsActiveRoute } from "../hooks/useIsActiveRoute";
10
11
  import { useRouter } from "../hooks/useRouter";
11
- import { useStableValue } from "../hooks/useStableValue";
12
12
 
13
13
  import type { LinkProps } from "../types";
14
14
  import type { FC, MouseEvent } from "react";
@@ -27,79 +27,76 @@ function areLinkPropsEqual(
27
27
  prev.target === next.target &&
28
28
  prev.style === next.style &&
29
29
  prev.children === next.children &&
30
- JSON.stringify(prev.routeParams) === JSON.stringify(next.routeParams) &&
31
- JSON.stringify(prev.routeOptions) === JSON.stringify(next.routeOptions)
30
+ shallowEqual(prev.routeParams, next.routeParams) &&
31
+ shallowEqual(prev.routeOptions, next.routeOptions)
32
32
  );
33
33
  }
34
34
 
35
- export const Link: FC<LinkProps> = memo(
36
- ({
37
- routeName,
38
- routeParams = EMPTY_PARAMS,
39
- routeOptions = EMPTY_OPTIONS,
40
- className,
41
- activeClassName = "active",
42
- activeStrict = false,
43
- ignoreQueryParams = true,
44
- onClick,
45
- target,
46
- children,
47
- ...props
48
- }) => {
49
- const router = useRouter();
35
+ const LinkImpl: FC<LinkProps> = ({
36
+ routeName,
37
+ routeParams = EMPTY_PARAMS,
38
+ routeOptions = EMPTY_OPTIONS,
39
+ className,
40
+ activeClassName = "active",
41
+ activeStrict = false,
42
+ ignoreQueryParams = true,
43
+ onClick,
44
+ target,
45
+ children,
46
+ ...props
47
+ }) => {
48
+ // memo + areLinkPropsEqual guarantees that on bail-out the component does
49
+ // not render; on render, routeParams/routeOptions either changed reference
50
+ // (true change) or comparator failed (e.g., BigInt fallback to identity),
51
+ // so they're safe to use directly in hook deps.
50
52
 
51
- const stableParams = useStableValue(routeParams);
52
- const stableOptions = useStableValue(routeOptions);
53
+ const router = useRouter();
53
54
 
54
- const isActive = useIsActiveRoute(
55
- routeName,
56
- stableParams,
57
- activeStrict,
58
- ignoreQueryParams,
59
- );
60
-
61
- const href = useMemo(
62
- () => buildHref(router, routeName, stableParams),
63
- [router, routeName, stableParams],
64
- );
55
+ const isActive = useIsActiveRoute(
56
+ routeName,
57
+ routeParams,
58
+ activeStrict,
59
+ ignoreQueryParams,
60
+ );
65
61
 
66
- const handleClick = useCallback(
67
- (evt: MouseEvent<HTMLAnchorElement>) => {
68
- if (onClick) {
69
- onClick(evt);
62
+ const href = useMemo(
63
+ () => buildHref(router, routeName, routeParams),
64
+ [router, routeName, routeParams],
65
+ );
70
66
 
71
- if (evt.defaultPrevented) {
72
- return;
73
- }
74
- }
67
+ const handleClick = useCallback(
68
+ (evt: MouseEvent<HTMLAnchorElement>) => {
69
+ if (onClick) {
70
+ onClick(evt);
75
71
 
76
- if (!shouldNavigate(evt.nativeEvent) || target === "_blank") {
72
+ if (evt.defaultPrevented) {
77
73
  return;
78
74
  }
75
+ }
79
76
 
80
- evt.preventDefault();
81
- router.navigate(routeName, stableParams, stableOptions).catch(() => {});
82
- },
83
- [onClick, target, router, routeName, stableParams, stableOptions],
84
- );
77
+ if (!shouldNavigate(evt.nativeEvent) || target === "_blank") {
78
+ return;
79
+ }
85
80
 
86
- const finalClassName = useMemo(
87
- () => buildActiveClassName(isActive, activeClassName, className),
88
- [isActive, activeClassName, className],
89
- );
81
+ evt.preventDefault();
82
+ router.navigate(routeName, routeParams, routeOptions).catch(() => {});
83
+ },
84
+ [onClick, target, router, routeName, routeParams, routeOptions],
85
+ );
86
+
87
+ const finalClassName = buildActiveClassName(
88
+ isActive,
89
+ activeClassName,
90
+ className,
91
+ );
92
+
93
+ return (
94
+ <a {...props} href={href} className={finalClassName} onClick={handleClick}>
95
+ {children}
96
+ </a>
97
+ );
98
+ };
90
99
 
91
- return (
92
- <a
93
- {...props}
94
- href={href}
95
- className={finalClassName}
96
- onClick={handleClick}
97
- >
98
- {children}
99
- </a>
100
- );
101
- },
102
- areLinkPropsEqual,
103
- );
100
+ export const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);
104
101
 
105
102
  Link.displayName = "Link";
@@ -1,6 +1,7 @@
1
- import { useCallback, useEffect, useRef, useState } from "react";
1
+ import { createDismissableError } from "@real-router/sources";
2
+ import { useEffect, useRef, useSyncExternalStore } from "react";
2
3
 
3
- import { useRouterError } from "../hooks/useRouterError";
4
+ import { useRouter } from "../hooks/useRouter";
4
5
 
5
6
  import type { RouterError, State } from "@real-router/core";
6
7
  import type { ReactNode, JSX } from "react";
@@ -20,8 +21,13 @@ export function RouterErrorBoundary({
20
21
  fallback,
21
22
  onError,
22
23
  }: RouterErrorBoundaryProps): JSX.Element {
23
- const snapshot = useRouterError();
24
- const [dismissedVersion, setDismissedVersion] = useState(-1);
24
+ const router = useRouter();
25
+ const store = createDismissableError(router);
26
+ const snapshot = useSyncExternalStore(
27
+ store.subscribe,
28
+ store.getSnapshot,
29
+ store.getSnapshot,
30
+ );
25
31
 
26
32
  const onErrorRef = useRef(onError);
27
33
 
@@ -39,17 +45,10 @@ export function RouterErrorBoundary({
39
45
  // eslint-disable-next-line @eslint-react/exhaustive-deps -- onError tracked via ref, snapshot fields accessed inside callback
40
46
  }, [snapshot.version]);
41
47
 
42
- const visibleError =
43
- snapshot.version > dismissedVersion ? snapshot.error : null;
44
-
45
- const resetError = useCallback(() => {
46
- setDismissedVersion(snapshot.version);
47
- }, [snapshot.version]);
48
-
49
48
  return (
50
49
  <>
51
50
  {children}
52
- {visibleError ? fallback(visibleError, resetError) : null}
51
+ {snapshot.error ? fallback(snapshot.error, snapshot.resetError) : null}
53
52
  </>
54
53
  );
55
54
  }
@@ -1,4 +1,4 @@
1
- import { useRef } from "react";
1
+ import { useMemo, useRef } from "react";
2
2
 
3
3
  import { Match, NotFound } from "./components";
4
4
  import { buildRenderList, collectElements } from "./helpers";
@@ -12,16 +12,26 @@ function RouteViewRoot({
12
12
  children,
13
13
  }: Readonly<RouteViewProps>): ReactElement | null {
14
14
  const { route } = useRouteNode(nodeName);
15
- const hasBeenActivatedRef = useRef<Set<string>>(new Set());
15
+ const hasBeenActivatedRef = useRef<Set<string> | null>(null);
16
+
17
+ // eslint-disable-next-line @eslint-react/refs -- lazy init: assign once when null to avoid `new Set()` allocation on every render
18
+ hasBeenActivatedRef.current ??= new Set();
19
+
20
+ // Skip the Children.toArray + collectElements traversal when children
21
+ // reference is unchanged. children only changes when the parent re-renders
22
+ // with a new ReactNode, so this caches the steady-state.
23
+ const elements = useMemo(() => {
24
+ const collected: ReactElement[] = [];
25
+
26
+ collectElements(children, collected);
27
+
28
+ return collected;
29
+ }, [children]);
16
30
 
17
31
  if (!route) {
18
32
  return null;
19
33
  }
20
34
 
21
- const elements: ReactElement[] = [];
22
-
23
- collectElements(children, elements);
24
-
25
35
  const { rendered } = buildRenderList(
26
36
  elements,
27
37
  route.name,
@@ -12,6 +12,10 @@ function isSegmentMatch(
12
12
  fullSegmentName: string,
13
13
  exact: boolean,
14
14
  ): boolean {
15
+ if (fullSegmentName === "") {
16
+ return false;
17
+ }
18
+
15
19
  if (exact) {
16
20
  return routeName === fullSegmentName;
17
21
  }
package/src/constants.ts CHANGED
@@ -1,4 +1,4 @@
1
- // packages/react/modules/constants.ts
1
+ // packages/react/src/constants.ts
2
2
 
3
3
  /**
4
4
  * Stable empty object for default params
package/src/context.ts CHANGED
@@ -1,4 +1,4 @@
1
- // packages/react/modules/context.ts
1
+ // packages/react/src/context.ts
2
2
 
3
3
  import { createContext } from "react";
4
4
 
@@ -1,8 +1,7 @@
1
1
  import { createActiveRouteSource } from "@real-router/sources";
2
- import { useMemo, useSyncExternalStore } from "react";
2
+ import { useSyncExternalStore } from "react";
3
3
 
4
4
  import { useRouter } from "./useRouter";
5
- import { useStableValue } from "./useStableValue";
6
5
 
7
6
  import type { Params } from "@real-router/core";
8
7
 
@@ -14,23 +13,17 @@ export function useIsActiveRoute(
14
13
  ): boolean {
15
14
  const router = useRouter();
16
15
 
17
- // useStableValue: JSON.stringify memoization of params object.
18
- // Without it, every render with a new params reference (e.g.,
19
- // <Link routeParams={{ id: '123' }} />) would recreate the store.
20
- const stableParams = useStableValue(params);
21
-
22
- const store = useMemo(
23
- () =>
24
- createActiveRouteSource(router, routeName, stableParams, {
25
- strict,
26
- ignoreQueryParams,
27
- }),
28
- [router, routeName, stableParams, strict, ignoreQueryParams],
29
- );
16
+ // createActiveRouteSource is per-router + canonical-args cached in
17
+ // @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
+ });
30
23
 
31
24
  return useSyncExternalStore(
32
25
  store.subscribe,
33
26
  store.getSnapshot,
34
- store.getSnapshot, // SSR: router returns same state on server and client
27
+ store.getSnapshot,
35
28
  );
36
29
  }
@@ -1,4 +1,4 @@
1
- // packages/react/modules/hooks/useNavigator.tsx
1
+ // packages/react/src/hooks/useNavigator.tsx
2
2
 
3
3
  import { useContext } from "react";
4
4
 
@@ -1,4 +1,4 @@
1
- // packages/react/modules/hooks/useRoute.tsx
1
+ // packages/react/src/hooks/useRoute.tsx
2
2
 
3
3
  import { useContext } from "react";
4
4
 
@@ -14,16 +14,24 @@ export function useRouteNode(nodeName: string): RouteContext {
14
14
  [router, nodeName],
15
15
  );
16
16
 
17
- const { route, previousRoute } = useSyncExternalStore(
17
+ // Use snapshot reference directly. createRouteNodeSource via stabilizeState
18
+ // returns the SAME snapshot when the node-relevant state did not change,
19
+ // so memoization on `[navigator, snapshot]` preserves identity for consumers.
20
+ const snapshot = useSyncExternalStore(
18
21
  store.subscribe,
19
22
  store.getSnapshot,
20
23
  store.getSnapshot, // SSR: router returns same state on server and client
21
24
  );
22
25
 
23
- const navigator = useMemo(() => getNavigator(router), [router]);
26
+ // getNavigator is WeakMap-cached in core; additional useMemo is redundant.
27
+ const navigator = getNavigator(router);
24
28
 
25
29
  return useMemo(
26
- (): RouteContext => ({ navigator, route, previousRoute }),
27
- [navigator, route, previousRoute],
30
+ (): RouteContext => ({
31
+ navigator,
32
+ route: snapshot.route,
33
+ previousRoute: snapshot.previousRoute,
34
+ }),
35
+ [navigator, snapshot],
28
36
  );
29
37
  }
@@ -1,5 +1,3 @@
1
- // packages/react/modules/hooks/useRouteUtils.tsx
2
-
3
1
  import { getPluginApi } from "@real-router/core/api";
4
2
  import { getRouteUtils } from "@real-router/route-utils";
5
3
 
@@ -10,8 +8,9 @@ import type { RouteUtils } from "@real-router/route-utils";
10
8
  /**
11
9
  * Returns a pre-computed {@link RouteUtils} instance for the current router.
12
10
  *
13
- * Internally retrieves the route tree via `getPluginApi` and delegates
14
- * to `getRouteUtils`, which caches instances per tree reference (WeakMap).
11
+ * `getRouteUtils` is WeakMap-cached per `RouteTreeNode` inside
12
+ * `@real-router/route-utils`, so the same router always returns the same
13
+ * `RouteUtils` instance across renders — no local cache needed in the adapter.
15
14
  *
16
15
  * @returns RouteUtils instance with pre-computed chains and siblings
17
16
  *
@@ -1,4 +1,4 @@
1
- // packages/react/modules/hooks/useRouter.tsx
1
+ // packages/react/src/hooks/useRouter.tsx
2
2
 
3
3
  import { useContext } from "react";
4
4
 
@@ -1,5 +1,5 @@
1
- import { createTransitionSource } from "@real-router/sources";
2
- import { useMemo, useSyncExternalStore } from "react";
1
+ import { getTransitionSource } from "@real-router/sources";
2
+ import { useSyncExternalStore } from "react";
3
3
 
4
4
  import { useRouter } from "./useRouter";
5
5
 
@@ -7,8 +7,7 @@ import type { RouterTransitionSnapshot } from "@real-router/sources";
7
7
 
8
8
  export function useRouterTransition(): RouterTransitionSnapshot {
9
9
  const router = useRouter();
10
-
11
- const store = useMemo(() => createTransitionSource(router), [router]);
10
+ const store = getTransitionSource(router);
12
11
 
13
12
  return useSyncExternalStore(
14
13
  store.subscribe,
@@ -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]),{route:o,previousRoute:s}=(0,e.useSyncExternalStore)(a.subscribe,a.getSnapshot,a.getSnapshot),c=(0,e.useMemo)(()=>(0,t.getNavigator)(r),[r]);return(0,e.useMemo)(()=>({navigator:c,route:o,previousRoute:s}),[c,o,s])}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,l=h(),u=setTimeout(()=>{a=!0},100),d=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=_(e,n,r);t&&t!==s&&a&&(s=t,clearTimeout(c),l.textContent=t,c=setTimeout(()=>{l.textContent=``,s=``},7e3),v())})})});return{destroy(){o=!0,d(),clearTimeout(c),clearTimeout(u),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){if(n)return n(e);let r=document.querySelector(`h1`)?.textContent.trim()??``,i=e.name.startsWith(`@@`)?``:e.name;return`${t}${r||document.title||i||globalThis.location.pathname}`}function v(){let e=document.querySelector(`h1`);e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}function y(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function b(e,t,n){try{let r=e.buildUrl;return r?r(t,n):e.buildPath(t,n)}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function x(e,t,n){return e&&t?n?`${n} ${t}`.trim():t:n??void 0}function S(t){return(0,e.useMemo)(()=>t,[JSON.stringify(t)])}function C(t,n,r=!1,a=!0){let o=l(),s=S(n),c=(0,e.useMemo)(()=>(0,i.createActiveRouteSource)(o,t,s,{strict:r,ignoreQueryParams:a}),[o,t,s,r,a]);return(0,e.useSyncExternalStore)(c.subscribe,c.getSnapshot,c.getSnapshot)}function w(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&&JSON.stringify(e.routeParams)===JSON.stringify(t.routeParams)&&JSON.stringify(e.routeOptions)===JSON.stringify(t.routeOptions)}const T=(0,e.memo)(({routeName:t,routeParams:n=d,routeOptions:i=f,className:a,activeClassName:o=`active`,activeStrict:s=!1,ignoreQueryParams:c=!0,onClick:u,target:p,children:m,...h})=>{let g=l(),_=S(n),v=S(i),w=C(t,_,s,c),T=(0,e.useMemo)(()=>b(g,t,_),[g,t,_]),E=(0,e.useCallback)(e=>{u&&(u(e),e.defaultPrevented)||!y(e.nativeEvent)||p===`_blank`||(e.preventDefault(),g.navigate(t,_,v).catch(()=>{}))},[u,p,g,t,_,v]),D=(0,e.useMemo)(()=>x(w,o,a),[w,o,a]);return(0,r.jsx)(`a`,{...h,href:T,className:D,onClick:E,children:m})},w);T.displayName=`Link`;const E=new WeakMap;function D(){let t=l(),n=(0,e.useMemo)(()=>{let e=E.get(t);return e||(e=(0,i.createErrorSource)(t),E.set(t,e)),e},[t]);return(0,e.useSyncExternalStore)(n.subscribe,n.getSnapshot,n.getSnapshot)}function O({children:t,fallback:n,onError:i}){let a=D(),[o,s]=(0,e.useState)(-1),c=(0,e.useRef)(i);c.current=i,(0,e.useEffect)(()=>{a.error&&c.current?.(a.error,a.toRoute,a.fromRoute)},[a.version]);let l=a.version>o?a.error:null,u=(0,e.useCallback)(()=>{s(a.version)},[a.version]);return(0,r.jsxs)(r.Fragment,{children:[t,l?n(l,u):null]})}const k=()=>{let t=(0,e.useContext)(c);if(!t)throw Error(`useNavigator must be used within a RouterProvider`);return t},A=()=>(0,n.getRouteUtils)((0,a.getPluginApi)(l()).getTree()),j=()=>{let t=(0,e.useContext)(o);if(!t)throw Error(`useRoute must be used within a RouteProvider`);return t};function M(){let t=l(),n=(0,e.useMemo)(()=>(0,i.createTransitionSource)(t),[t]);return(0,e.useSyncExternalStore)(n.subscribe,n.getSnapshot,n.getSnapshot)}const N=({router:n,children:a,announceNavigation:l})=>{(0,e.useEffect)(()=>{if(!l)return;let e=m(n);return()=>{e.destroy()}},[l,n]);let u=(0,e.useMemo)(()=>(0,t.getNavigator)(n),[n]),d=(0,e.useMemo)(()=>(0,i.createRouteSource)(n),[n]),{route:f,previousRoute:p}=(0,e.useSyncExternalStore)(d.subscribe,d.getSnapshot,d.getSnapshot);return(0,r.jsx)(s,{value:n,children:(0,r.jsx)(c,{value:u,children:(0,r.jsx)(o,{value:(0,e.useMemo)(()=>({navigator:u,route:f,previousRoute:p}),[u,f,p]),children:a})})})};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return N}});
2
- //# sourceMappingURL=RouterProvider-jqJCKUhp.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RouterProvider-jqJCKUhp.js","names":[],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useStableValue.tsx","../../src/hooks/useIsActiveRoute.tsx","../../src/components/Link.tsx","../../src/hooks/useRouterError.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/modules/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/modules/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 const { route, previousRoute } = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n return useMemo(\n (): RouteContext => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n}\n","// packages/react/modules/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 clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const text = resolveText(route, prefix, getCustomText);\n\n if (text && text !== lastAnnouncedText && isReady) {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus();\n }\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,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\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(): void {\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n\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, 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;\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 return buildUrl(routeName, routeParams);\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\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n return baseClassName\n ? `${baseClassName} ${activeClassName}`.trim()\n : activeClassName;\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function applyLinkA11y(element: HTMLElement): void {\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.getAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.getAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","// packages/react/modules/hooks/useStableValue.tsx\n\nimport { useMemo } from \"react\";\n\n/**\n * Stabilizes a value reference based on deep equality (via JSON serialization).\n * Returns the same reference until the serialized value changes.\n *\n * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect\n * to prevent unnecessary re-renders when the value is structurally the same.\n *\n * @example\n * ```tsx\n * const stableParams = useStableValue(routeParams);\n * const href = useMemo(() => {\n * return router.buildUrl(routeName, stableParams);\n * }, [router, routeName, stableParams]);\n * ```\n *\n * @param value - The value to stabilize\n * @returns A stable reference to the value\n */\nexport function useStableValue<T>(value: T): T {\n const serialized = JSON.stringify(value);\n\n // We intentionally use serialized in deps to detect deep changes\n // eslint-disable-next-line @eslint-react/exhaustive-deps\n return useMemo(() => value, [serialized]);\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\nimport { useStableValue } from \"./useStableValue\";\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 // useStableValue: JSON.stringify memoization of params object.\n // Without it, every render with a new params reference (e.g.,\n // <Link routeParams={{ id: '123' }} />) would recreate the store.\n const stableParams = useStableValue(params);\n\n const store = useMemo(\n () =>\n createActiveRouteSource(router, routeName, stableParams, {\n strict,\n ignoreQueryParams,\n }),\n [router, routeName, stableParams, strict, ignoreQueryParams],\n );\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n}\n","import { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n} from \"../dom-utils/index.js\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\nimport { useStableValue } from \"../hooks/useStableValue\";\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 JSON.stringify(prev.routeParams) === JSON.stringify(next.routeParams) &&\n JSON.stringify(prev.routeOptions) === JSON.stringify(next.routeOptions)\n );\n}\n\nexport const Link: FC<LinkProps> = memo(\n ({\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 const router = useRouter();\n\n const stableParams = useStableValue(routeParams);\n const stableOptions = useStableValue(routeOptions);\n\n const isActive = useIsActiveRoute(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n const href = useMemo(\n () => buildHref(router, routeName, stableParams),\n [router, routeName, stableParams],\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, stableParams, stableOptions).catch(() => {});\n },\n [onClick, target, router, routeName, stableParams, stableOptions],\n );\n\n const finalClassName = useMemo(\n () => buildActiveClassName(isActive, activeClassName, className),\n [isActive, activeClassName, className],\n );\n\n return (\n <a\n {...props}\n href={href}\n className={finalClassName}\n onClick={handleClick}\n >\n {children}\n </a>\n );\n },\n areLinkPropsEqual,\n);\n\nLink.displayName = \"Link\";\n","import { createErrorSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { RouterErrorSnapshot, RouterSource } from \"@real-router/sources\";\n\nconst cache = new WeakMap<Router, RouterSource<RouterErrorSnapshot>>();\n\nexport function useRouterError(): RouterErrorSnapshot {\n const router = useRouter();\n const store = useMemo(() => {\n let source = cache.get(router);\n\n if (!source) {\n source = createErrorSource(router);\n cache.set(router, source);\n }\n\n return source;\n }, [router]);\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useRouterError } from \"../hooks/useRouterError\";\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 snapshot = useRouterError();\n const [dismissedVersion, setDismissedVersion] = useState(-1);\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 const visibleError =\n snapshot.version > dismissedVersion ? snapshot.error : null;\n\n const resetError = useCallback(() => {\n setDismissedVersion(snapshot.version);\n }, [snapshot.version]);\n\n return (\n <>\n {children}\n {visibleError ? fallback(visibleError, resetError) : null}\n </>\n );\n}\n","// packages/react/modules/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","// packages/react/modules/hooks/useRouteUtils.tsx\n\nimport { 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 * Internally retrieves the route tree via `getPluginApi` and delegates\n * to `getRouteUtils`, which caches instances per tree reference (WeakMap).\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/modules/hooks/useRoute.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\n};\n","import { createTransitionSource } from \"@real-router/sources\";\nimport { useMemo, 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\n const store = useMemo(() => createTransitionSource(router), [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 { createRouteAnnouncer } from \"./dom-utils/index.js\";\n\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}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\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 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 const { route, previousRoute } = 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 () => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n\n return (\n <RouterContext value={router}>\n <NavigatorContext value={navigator}>\n <RouteContext value={routeContextValue}>{children}</RouteContext>\n </NavigatorContext>\n </RouterContext>\n );\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{createContext as e,memo as t,useCallback as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useState as s,useSyncExternalStore as c}from"react";import{getNavigator as l}from"@real-router/core";import{getRouteUtils as u}from"@real-router/route-utils";import{Fragment as d,jsx as f,jsxs as p}from"react/jsx-runtime";import{createActiveRouteSource as m,createErrorSource as h,createRouteNodeSource as g,createRouteSource as _,createTransitionSource as v}from"@real-router/sources";import{getPluginApi as y}from"@real-router/core/api";const b=e(null),x=e(null),S=e(null),C=()=>{let e=r(x);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e};function w(e){let t=C(),n=a(()=>g(t,e),[t,e]),{route:r,previousRoute:i}=c(n.subscribe,n.getSnapshot,n.getSnapshot),o=a(()=>l(t),[t]);return a(()=>({navigator:o,route:r,previousRoute:i}),[o,r,i])}const T=Object.freeze({}),E=Object.freeze({}),D=`data-real-router-announcer`;function O(e,t){let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c,l=k(),u=setTimeout(()=>{a=!0},100),d=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=j(e,n,r);t&&t!==s&&a&&(s=t,clearTimeout(c),l.textContent=t,c=setTimeout(()=>{l.textContent=``,s=``},7e3),M())})})});return{destroy(){o=!0,d(),clearTimeout(c),clearTimeout(u),A()}}}function k(){let e=document.querySelector(`[${D}]`);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(D,``),document.body.prepend(t),t}function A(){document.querySelector(`[${D}]`)?.remove()}function j(e,t,n){if(n)return n(e);let r=document.querySelector(`h1`)?.textContent.trim()??``,i=e.name.startsWith(`@@`)?``:e.name;return`${t}${r||document.title||i||globalThis.location.pathname}`}function M(){let e=document.querySelector(`h1`);e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}function N(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}function P(e,t,n){try{let r=e.buildUrl;return r?r(t,n):e.buildPath(t,n)}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function F(e,t,n){return e&&t?n?`${n} ${t}`.trim():t:n??void 0}function I(e){return a(()=>e,[JSON.stringify(e)])}function L(e,t,n=!1,r=!0){let i=C(),o=I(t),s=a(()=>m(i,e,o,{strict:n,ignoreQueryParams:r}),[i,e,o,n,r]);return c(s.subscribe,s.getSnapshot,s.getSnapshot)}function R(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&&JSON.stringify(e.routeParams)===JSON.stringify(t.routeParams)&&JSON.stringify(e.routeOptions)===JSON.stringify(t.routeOptions)}const z=t(({routeName:e,routeParams:t=T,routeOptions:r=E,className:i,activeClassName:o=`active`,activeStrict:s=!1,ignoreQueryParams:c=!0,onClick:l,target:u,children:d,...p})=>{let m=C(),h=I(t),g=I(r),_=L(e,h,s,c),v=a(()=>P(m,e,h),[m,e,h]),y=n(t=>{l&&(l(t),t.defaultPrevented)||!N(t.nativeEvent)||u===`_blank`||(t.preventDefault(),m.navigate(e,h,g).catch(()=>{}))},[l,u,m,e,h,g]),b=a(()=>F(_,o,i),[_,o,i]);return f(`a`,{...p,href:v,className:b,onClick:y,children:d})},R);z.displayName=`Link`;const B=new WeakMap;function V(){let e=C(),t=a(()=>{let t=B.get(e);return t||(t=h(e),B.set(e,t)),t},[e]);return c(t.subscribe,t.getSnapshot,t.getSnapshot)}function H({children:e,fallback:t,onError:r}){let a=V(),[c,l]=s(-1),u=o(r);u.current=r,i(()=>{a.error&&u.current?.(a.error,a.toRoute,a.fromRoute)},[a.version]);let f=a.version>c?a.error:null,m=n(()=>{l(a.version)},[a.version]);return p(d,{children:[e,f?t(f,m):null]})}const U=()=>{let e=r(S);if(!e)throw Error(`useNavigator must be used within a RouterProvider`);return e},W=()=>u(y(C()).getTree()),G=()=>{let e=r(b);if(!e)throw Error(`useRoute must be used within a RouteProvider`);return e};function K(){let e=C(),t=a(()=>v(e),[e]);return c(t.subscribe,t.getSnapshot,t.getSnapshot)}const q=({router:e,children:t,announceNavigation:n})=>{i(()=>{if(!n)return;let t=O(e);return()=>{t.destroy()}},[n,e]);let r=a(()=>l(e),[e]),o=a(()=>_(e),[e]),{route:s,previousRoute:u}=c(o.subscribe,o.getSnapshot,o.getSnapshot);return f(x,{value:e,children:f(S,{value:r,children:f(b,{value:a(()=>({navigator:r,route:s,previousRoute:u}),[r,s,u]),children:t})})})};export{U as a,w as c,W as i,C as l,K as n,H as o,G as r,z as s,q as t};
2
- //# sourceMappingURL=RouterProvider-DrAGH6K9.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RouterProvider-DrAGH6K9.mjs","names":[],"sources":["../../src/context.ts","../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../src/constants.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useStableValue.tsx","../../src/hooks/useIsActiveRoute.tsx","../../src/components/Link.tsx","../../src/hooks/useRouterError.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/modules/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/modules/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 const { route, previousRoute } = useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n\n const navigator = useMemo(() => getNavigator(router), [router]);\n\n return useMemo(\n (): RouteContext => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n}\n","// packages/react/modules/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 clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const announcer = getOrCreateAnnouncer();\n\n const safariTimeoutId = setTimeout(() => {\n isReady = true;\n }, SAFARI_READY_DELAY);\n\n const unsubscribe = router.subscribe(({ route }) => {\n if (isInitialNavigation) {\n isInitialNavigation = false;\n\n return;\n }\n\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (isDestroyed) {\n return;\n }\n\n const text = resolveText(route, prefix, getCustomText);\n\n if (text && text !== lastAnnouncedText && isReady) {\n lastAnnouncedText = text;\n clearTimeout(clearTimeoutId);\n announcer.textContent = text;\n clearTimeoutId = setTimeout(() => {\n announcer.textContent = \"\";\n lastAnnouncedText = \"\";\n }, CLEAR_DELAY);\n\n manageFocus();\n }\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,\n): string {\n if (getCustomText) {\n return getCustomText(route);\n }\n\n const h1 = document.querySelector<HTMLElement>(\"h1\");\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(): void {\n const h1 = document.querySelector<HTMLElement>(\"h1\");\n\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, 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;\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 return buildUrl(routeName, routeParams);\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\nexport function buildActiveClassName(\n isActive: boolean,\n activeClassName: string | undefined,\n baseClassName: string | undefined,\n): string | undefined {\n if (isActive && activeClassName) {\n return baseClassName\n ? `${baseClassName} ${activeClassName}`.trim()\n : activeClassName;\n }\n\n return baseClassName ?? undefined;\n}\n\nexport function applyLinkA11y(element: HTMLElement): void {\n if (\n element instanceof HTMLAnchorElement ||\n element instanceof HTMLButtonElement\n ) {\n return;\n }\n if (!element.getAttribute(\"role\")) {\n element.setAttribute(\"role\", \"link\");\n }\n if (!element.getAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n }\n}\n","// packages/react/modules/hooks/useStableValue.tsx\n\nimport { useMemo } from \"react\";\n\n/**\n * Stabilizes a value reference based on deep equality (via JSON serialization).\n * Returns the same reference until the serialized value changes.\n *\n * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect\n * to prevent unnecessary re-renders when the value is structurally the same.\n *\n * @example\n * ```tsx\n * const stableParams = useStableValue(routeParams);\n * const href = useMemo(() => {\n * return router.buildUrl(routeName, stableParams);\n * }, [router, routeName, stableParams]);\n * ```\n *\n * @param value - The value to stabilize\n * @returns A stable reference to the value\n */\nexport function useStableValue<T>(value: T): T {\n const serialized = JSON.stringify(value);\n\n // We intentionally use serialized in deps to detect deep changes\n // eslint-disable-next-line @eslint-react/exhaustive-deps\n return useMemo(() => value, [serialized]);\n}\n","import { createActiveRouteSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\nimport { useStableValue } from \"./useStableValue\";\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 // useStableValue: JSON.stringify memoization of params object.\n // Without it, every render with a new params reference (e.g.,\n // <Link routeParams={{ id: '123' }} />) would recreate the store.\n const stableParams = useStableValue(params);\n\n const store = useMemo(\n () =>\n createActiveRouteSource(router, routeName, stableParams, {\n strict,\n ignoreQueryParams,\n }),\n [router, routeName, stableParams, strict, ignoreQueryParams],\n );\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot, // SSR: router returns same state on server and client\n );\n}\n","import { memo, useCallback, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n} from \"../dom-utils/index.js\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\nimport { useStableValue } from \"../hooks/useStableValue\";\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 JSON.stringify(prev.routeParams) === JSON.stringify(next.routeParams) &&\n JSON.stringify(prev.routeOptions) === JSON.stringify(next.routeOptions)\n );\n}\n\nexport const Link: FC<LinkProps> = memo(\n ({\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 const router = useRouter();\n\n const stableParams = useStableValue(routeParams);\n const stableOptions = useStableValue(routeOptions);\n\n const isActive = useIsActiveRoute(\n routeName,\n stableParams,\n activeStrict,\n ignoreQueryParams,\n );\n\n const href = useMemo(\n () => buildHref(router, routeName, stableParams),\n [router, routeName, stableParams],\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, stableParams, stableOptions).catch(() => {});\n },\n [onClick, target, router, routeName, stableParams, stableOptions],\n );\n\n const finalClassName = useMemo(\n () => buildActiveClassName(isActive, activeClassName, className),\n [isActive, activeClassName, className],\n );\n\n return (\n <a\n {...props}\n href={href}\n className={finalClassName}\n onClick={handleClick}\n >\n {children}\n </a>\n );\n },\n areLinkPropsEqual,\n);\n\nLink.displayName = \"Link\";\n","import { createErrorSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { RouterErrorSnapshot, RouterSource } from \"@real-router/sources\";\n\nconst cache = new WeakMap<Router, RouterSource<RouterErrorSnapshot>>();\n\nexport function useRouterError(): RouterErrorSnapshot {\n const router = useRouter();\n const store = useMemo(() => {\n let source = cache.get(router);\n\n if (!source) {\n source = createErrorSource(router);\n cache.set(router, source);\n }\n\n return source;\n }, [router]);\n\n return useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getSnapshot,\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useRouterError } from \"../hooks/useRouterError\";\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 snapshot = useRouterError();\n const [dismissedVersion, setDismissedVersion] = useState(-1);\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 const visibleError =\n snapshot.version > dismissedVersion ? snapshot.error : null;\n\n const resetError = useCallback(() => {\n setDismissedVersion(snapshot.version);\n }, [snapshot.version]);\n\n return (\n <>\n {children}\n {visibleError ? fallback(visibleError, resetError) : null}\n </>\n );\n}\n","// packages/react/modules/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","// packages/react/modules/hooks/useRouteUtils.tsx\n\nimport { 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 * Internally retrieves the route tree via `getPluginApi` and delegates\n * to `getRouteUtils`, which caches instances per tree reference (WeakMap).\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/modules/hooks/useRoute.tsx\n\nimport { useContext } from \"react\";\n\nimport { RouteContext } from \"../context\";\n\nimport type { RouteContext as RouteContextType } from \"../types\";\n\nexport const useRoute = (): RouteContextType => {\n const routeContext = useContext(RouteContext);\n\n if (!routeContext) {\n throw new Error(\"useRoute must be used within a RouteProvider\");\n }\n\n return routeContext;\n};\n","import { createTransitionSource } from \"@real-router/sources\";\nimport { useMemo, 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\n const store = useMemo(() => createTransitionSource(router), [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 { createRouteAnnouncer } from \"./dom-utils/index.js\";\n\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}\n\nexport const RouterProvider: FC<RouteProviderProps> = ({\n router,\n children,\n announceNavigation,\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 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 const { route, previousRoute } = 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 () => ({ navigator, route, previousRoute }),\n [navigator, route, previousRoute],\n );\n\n return (\n <RouterContext value={router}>\n <NavigatorContext value={navigator}>\n <RouteContext value={routeContextValue}>{children}</RouteContext>\n </NavigatorContext>\n </RouterContext>\n );\n};\n"],"mappings":"oiBAOA,MAAa,EAAe,EAAuC,KAAK,CAE3D,EAAgB,EAA6B,KAAK,CAElD,EAAmB,EAAgC,KAAK,CCHxD,MAA0B,CACrC,IAAM,EAAS,EAAW,EAAc,CAExC,GAAI,CAAC,EACH,MAAU,MAAM,iDAAiD,CAGnE,OAAO,GCPT,SAAgB,EAAa,EAAgC,CAC3D,IAAM,EAAS,GAAW,CAEpB,EAAQ,MACN,EAAsB,EAAQ,EAAS,CAC7C,CAAC,EAAQ,EAAS,CACnB,CAEK,CAAE,QAAO,iBAAkB,EAC/B,EAAM,UACN,EAAM,YACN,EAAM,YACP,CAEK,EAAY,MAAc,EAAa,EAAO,CAAE,CAAC,EAAO,CAAC,CAE/D,OAAO,OACgB,CAAE,YAAW,QAAO,gBAAe,EACxD,CAAC,EAAW,EAAO,EAAc,CAClC,CCtBH,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,EAEE,EAAY,GAAsB,CAElC,EAAkB,eAAiB,CACvC,EAAU,IACT,IAAmB,CAEhB,EAAc,EAAO,WAAW,CAAE,WAAY,CAClD,GAAI,EAAqB,CACvB,EAAsB,GAEtB,OAGF,0BAA4B,CAC1B,0BAA4B,CAC1B,GAAI,EACF,OAGF,IAAM,EAAO,EAAY,EAAO,EAAQ,EAAc,CAElD,GAAQ,IAAS,GAAqB,IACxC,EAAoB,EACpB,aAAa,EAAe,CAC5B,EAAU,YAAc,EACxB,EAAiB,eAAiB,CAChC,EAAU,YAAc,GACxB,EAAoB,IACnB,IAAY,CAEf,GAAa,GAEf,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,EACQ,CACR,GAAI,EACF,OAAO,EAAc,EAAM,CAI7B,IAAM,EADK,SAAS,cAA2B,KAAK,EACjC,YAAY,MAAM,EAAI,GACnC,EAAY,EAAM,KAAK,WAAW,KAAsB,CAC1D,GACA,EAAM,KAIV,MAAO,GAAG,IAFR,GAAU,SAAS,OAAS,GAAa,WAAW,SAAS,WAKjE,SAAS,GAAoB,CAC3B,IAAM,EAAK,SAAS,cAA2B,KAAK,CAE/C,IAIA,EAAG,aAAa,WAAW,EAC9B,EAAG,aAAa,WAAY,KAAK,CAGnC,EAAG,MAAM,CAAE,cAAe,GAAM,CAAC,EC9HnC,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,SAMxB,OAJI,EACK,EAAS,EAAW,EAAY,CAGlC,EAAO,UAAU,EAAW,EAAY,MACzC,CACN,QAAQ,MACN,wBAAwB,EAAU,sEACnC,CAED,QAIJ,SAAgB,EACd,EACA,EACA,EACoB,CAOpB,OANI,GAAY,EACP,EACH,GAAG,EAAc,GAAG,IAAkB,MAAM,CAC5C,EAGC,GAAiB,IAAA,GCzB1B,SAAgB,EAAkB,EAAa,CAK7C,OAAO,MAAc,EAAO,CAJT,KAAK,UAAU,EAAM,CAIA,CAAC,CCnB3C,SAAgB,EACd,EACA,EACA,EAAS,GACT,EAAoB,GACX,CACT,IAAM,EAAS,GAAW,CAKpB,EAAe,EAAe,EAAO,CAErC,EAAQ,MAEV,EAAwB,EAAQ,EAAW,EAAc,CACvD,SACA,oBACD,CAAC,CACJ,CAAC,EAAQ,EAAW,EAAc,EAAQ,EAAkB,CAC7D,CAED,OAAO,EACL,EAAM,UACN,EAAM,YACN,EAAM,YACP,CCnBH,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,KAAK,UAAU,EAAK,YAAY,GAAK,KAAK,UAAU,EAAK,YAAY,EACrE,KAAK,UAAU,EAAK,aAAa,GAAK,KAAK,UAAU,EAAK,aAAa,CAI3E,MAAa,EAAsB,GAChC,CACC,YACA,cAAc,EACd,eAAe,EACf,YACA,kBAAkB,SAClB,eAAe,GACf,oBAAoB,GACpB,UACA,SACA,WACA,GAAG,KACC,CACJ,IAAM,EAAS,GAAW,CAEpB,EAAe,EAAe,EAAY,CAC1C,EAAgB,EAAe,EAAa,CAE5C,EAAW,EACf,EACA,EACA,EACA,EACD,CAEK,EAAO,MACL,EAAU,EAAQ,EAAW,EAAa,CAChD,CAAC,EAAQ,EAAW,EAAa,CAClC,CAEK,EAAc,EACjB,GAAuC,CAClC,IACF,EAAQ,EAAI,CAER,EAAI,mBAKN,CAAC,EAAe,EAAI,YAAY,EAAI,IAAW,WAInD,EAAI,gBAAgB,CACpB,EAAO,SAAS,EAAW,EAAc,EAAc,CAAC,UAAY,GAAG,GAEzE,CAAC,EAAS,EAAQ,EAAQ,EAAW,EAAc,EAAc,CAClE,CAEK,EAAiB,MACf,EAAqB,EAAU,EAAiB,EAAU,CAChE,CAAC,EAAU,EAAiB,EAAU,CACvC,CAED,OACE,EAAC,IAAD,CACE,GAAI,EACE,OACN,UAAW,EACX,QAAS,EAER,WACC,CAAA,EAGR,EACD,CAED,EAAK,YAAc,OChGnB,MAAM,EAAQ,IAAI,QAElB,SAAgB,GAAsC,CACpD,IAAM,EAAS,GAAW,CACpB,EAAQ,MAAc,CAC1B,IAAI,EAAS,EAAM,IAAI,EAAO,CAO9B,OALK,IACH,EAAS,EAAkB,EAAO,CAClC,EAAM,IAAI,EAAQ,EAAO,EAGpB,GACN,CAAC,EAAO,CAAC,CAEZ,OAAO,EACL,EAAM,UACN,EAAM,YACN,EAAM,YACP,CCVH,SAAgB,EAAoB,CAClC,WACA,WACA,WACwC,CACxC,IAAM,EAAW,GAAgB,CAC3B,CAAC,EAAkB,GAAuB,EAAS,GAAG,CAEtD,EAAa,EAAO,EAAQ,CAGlC,EAAW,QAAU,EAErB,MAAgB,CACV,EAAS,OACX,EAAW,UACT,EAAS,MACT,EAAS,QACT,EAAS,UACV,EAGF,CAAC,EAAS,QAAQ,CAAC,CAEtB,IAAM,EACJ,EAAS,QAAU,EAAmB,EAAS,MAAQ,KAEnD,EAAa,MAAkB,CACnC,EAAoB,EAAS,QAAQ,EACpC,CAAC,EAAS,QAAQ,CAAC,CAEtB,OACE,EAAA,EAAA,CAAA,SAAA,CACG,EACA,EAAe,EAAS,EAAc,EAAW,CAAG,KACpD,CAAA,CAAA,CC5CP,MAAa,MAAgC,CAC3C,IAAM,EAAY,EAAW,EAAiB,CAE9C,GAAI,CAAC,EACH,MAAU,MAAM,oDAAoD,CAGtE,OAAO,GCgBI,MAGJ,EAAc,EAFN,GAAW,CAEe,CAAC,SAAS,CAAC,CC1BzC,MAAmC,CAC9C,IAAM,EAAe,EAAW,EAAa,CAE7C,GAAI,CAAC,EACH,MAAU,MAAM,+CAA+C,CAGjE,OAAO,GCRT,SAAgB,GAAgD,CAC9D,IAAM,EAAS,GAAW,CAEpB,EAAQ,MAAc,EAAuB,EAAO,CAAE,CAAC,EAAO,CAAC,CAErE,OAAO,EACL,EAAM,UACN,EAAM,YACN,EAAM,YACP,CCAH,MAAa,GAA0C,CACrD,SACA,WACA,wBACI,CACJ,MAAgB,CACd,GAAI,CAAC,EACH,OAGF,IAAM,EAAY,EAAqB,EAAO,CAE9C,UAAa,CACX,EAAU,SAAS,GAEpB,CAAC,EAAoB,EAAO,CAAC,CAEhC,IAAM,EAAY,MAAc,EAAa,EAAO,CAAE,CAAC,EAAO,CAAC,CAKzD,EAAQ,MAAc,EAAkB,EAAO,CAAE,CAAC,EAAO,CAAC,CAC1D,CAAE,QAAO,iBAAkB,EAC/B,EAAM,UACN,EAAM,YACN,EAAM,YACP,CAOD,OACE,EAAC,EAAD,CAAe,MAAO,WACpB,EAAC,EAAD,CAAkB,MAAO,WACvB,EAAC,EAAD,CAAc,MARM,OACjB,CAAE,YAAW,QAAO,gBAAe,EAC1C,CAAC,EAAW,EAAO,EAAc,CAClC,CAK8C,WAAwB,CAAA,CAChD,CAAA,CACL,CAAA"}
@@ -1,29 +0,0 @@
1
- import { createErrorSource } from "@real-router/sources";
2
- import { useMemo, useSyncExternalStore } from "react";
3
-
4
- import { useRouter } from "./useRouter";
5
-
6
- import type { Router } from "@real-router/core";
7
- import type { RouterErrorSnapshot, RouterSource } from "@real-router/sources";
8
-
9
- const cache = new WeakMap<Router, RouterSource<RouterErrorSnapshot>>();
10
-
11
- export function useRouterError(): RouterErrorSnapshot {
12
- const router = useRouter();
13
- const store = useMemo(() => {
14
- let source = cache.get(router);
15
-
16
- if (!source) {
17
- source = createErrorSource(router);
18
- cache.set(router, source);
19
- }
20
-
21
- return source;
22
- }, [router]);
23
-
24
- return useSyncExternalStore(
25
- store.subscribe,
26
- store.getSnapshot,
27
- store.getSnapshot,
28
- );
29
- }
@@ -1,29 +0,0 @@
1
- // packages/react/modules/hooks/useStableValue.tsx
2
-
3
- import { useMemo } from "react";
4
-
5
- /**
6
- * Stabilizes a value reference based on deep equality (via JSON serialization).
7
- * Returns the same reference until the serialized value changes.
8
- *
9
- * Useful for object/array dependencies in hooks like useMemo, useCallback, useEffect
10
- * to prevent unnecessary re-renders when the value is structurally the same.
11
- *
12
- * @example
13
- * ```tsx
14
- * const stableParams = useStableValue(routeParams);
15
- * const href = useMemo(() => {
16
- * return router.buildUrl(routeName, stableParams);
17
- * }, [router, routeName, stableParams]);
18
- * ```
19
- *
20
- * @param value - The value to stabilize
21
- * @returns A stable reference to the value
22
- */
23
- export function useStableValue<T>(value: T): T {
24
- const serialized = JSON.stringify(value);
25
-
26
- // We intentionally use serialized in deps to detect deep changes
27
- // eslint-disable-next-line @eslint-react/exhaustive-deps
28
- return useMemo(() => value, [serialized]);
29
- }