{"version":3,"file":"RouterProviderCore-BQA-I4NQ.mjs","names":[],"sources":["../../src/hooks/useRouter.tsx","../../src/hooks/useRouteNode.tsx","../../../../shared/dom-utils/link-utils.ts","../../src/hooks/useIsActiveRoute.tsx","../../src/components/RouterErrorBoundary.tsx","../../src/hooks/useNavigator.tsx","../../src/hooks/useRouteUtils.tsx","../../src/hooks/useRouterTransition.tsx","../../src/RouterProviderCore.tsx"],"sourcesContent":["import { 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","import { getPluginApi } from \"@real-router/core/api\";\nimport { putField } from \"@real-router/core/utils\";\n\nimport type {\n  NavigationOptions,\n  NavigationTarget,\n  Params,\n  Router,\n  SearchParams,\n  State,\n} from \"@real-router/core\";\n\n/**\n * Intrinsics captured at module load (#1971).\n *\n * ⚑ These DECIDE — they answer \"what is on this object\" for a value this module\n * did not build. Read off the live global they can be re-pointed after boot, and\n * `shared/` is the half where that fails OPEN: measured in `browser-env`, a\n * re-pointed `getPrototypeOf` admits a `Date` into `state.params` and a\n * re-pointed `keys` skips option validation entirely.\n *\n * ⚠ Capture narrows the window from \"any time after boot\" to \"before this module\n * loads\". It does not close it — a shim evaluated ahead of core still wins\n * (#1798), which is the doctrine's own caveat and travels with it.\n */\nconst objectKeys = Object.keys;\n\n/**\n * The brand `packages/core/src/internals.ts` puts on every router it registers,\n * RE-DECLARED rather than imported (#2294).\n *\n * ⚑ `Symbol.for` makes the two the same symbol, so **the STRING is the\n * contract** — change it in one place and this warning silently stops firing.\n * The same shape core already uses for `CONFIG_FAULT`, declared twice across a\n * boundary it cannot import over, and pinned the same way: the guard is\n * `foreign-router-href-2294.test.ts`, whose cells fail on exactly that drift.\n */\nconst ROUTER_BRAND = Symbol.for(\"real-router.router\");\n\n/**\n * Is this a real router that core cannot read — as opposed to the `Router`-SHAPED\n * double this helper accepts by contract?\n *\n * ⚠ Read defensively: the argument is the caller's object and may be a `Proxy`\n * whose `get` trap throws. A diagnostic that throws would change where the error\n * comes FROM, which is the #1572 class, so a throwing read means \"cannot tell\".\n */\nfunction isUnreadableRouter(candidate: unknown): boolean {\n  try {\n    return (\n      (candidate as Record<symbol, unknown> | null | undefined)?.[\n        ROUTER_BRAND\n      ] === true\n    );\n  } catch {\n    return false;\n  }\n}\n\n/**\n * The registry lookup, ALONE — and standing alone is what makes the warning\n * correct (#2294).\n *\n * ⚑ Inside the resolve-and-print `try` this could not be told apart from a\n * `forwardState` that threw for a reason the arm handles deliberately. The\n * channel guard (#1572) is exactly that, and it fires on a router that IS\n * registered and IS branded — so a check made there would report duplicated core\n * on a perfectly healthy one.\n */\nfunction readPluginApi(\n  router: Router,\n  routeName: string,\n): ReturnType<typeof getPluginApi> | undefined {\n  try {\n    return getPluginApi(router);\n  } catch {\n    if (isUnreadableRouter(router)) {\n      console.error(\n        `[real-router] Route \"${routeName}\" rendered its LITERAL path: this IS a router, ` +\n          \"but not one this copy of @real-router/core built, so `forwardTo` was not resolved. \" +\n          \"It is either wrapped in a Proxy (Vue `reactive()` / Pinia — store it with `markRaw`), \" +\n          \"or your dependency tree holds two copies of @real-router/core — dedupe it to one.\",\n      );\n    }\n\n    return undefined;\n  }\n}\n\n/**\n * Resolved navigation channels for a `<Link>` — the single `{ name, params,\n * search }` shape every adapter feeds into `buildHref` / `navigateWithHash` /\n * the active-route source, regardless of which prop form the consumer used.\n */\nexport interface ResolvedLinkTarget {\n  name: string;\n  params: Params | undefined;\n  search: SearchParams | undefined;\n}\n\n/**\n * Collapses a `<Link>`'s two prop forms (RFC-4 M2 B2, #1548) into one channel\n * triple:\n *\n * - **Descriptor** — `to={{ name, params?, search? }}` (a `NavigationTarget`).\n * - **Channel props** — `routeName` + `routeParams?` + `routeSearch?`.\n *\n * The forms are mutually exclusive: the TS union on each adapter's `LinkProps`\n * rejects mixing them at compile time, and this helper is the runtime backstop —\n * when `to` is present it **wins**, and a `dev`-visible `console.warn` fires if\n * channel props were also supplied (a JS consumer, an object spread, or an\n * adapter without a strict union can still slip both through). `routeOptions` /\n * `hash` are separate props under BOTH forms (hash is not part of\n * `NavigationTarget` — #532), so they are resolved by the caller, not here.\n */\nexport function resolveLinkTarget(\n  to: NavigationTarget | undefined,\n  routeName: string,\n  routeParams: Params | undefined,\n  routeSearch: SearchParams | undefined,\n): ResolvedLinkTarget {\n  if (to !== undefined) {\n    if (\n      routeName !== \"\" ||\n      routeParams !== undefined ||\n      routeSearch !== undefined\n    ) {\n      console.warn(\n        \"[real-router] <Link> received both `to` and channel props \" +\n          \"(routeName / routeParams / routeSearch). `to` wins; the channel \" +\n          \"props are ignored. Use one form or the other.\",\n      );\n    }\n\n    return { name: to.name, params: to.params, search: to.search };\n  }\n\n  return { name: routeName, params: routeParams, search: routeSearch };\n}\n\nexport function shouldNavigate(evt: MouseEvent): boolean {\n  return (\n    evt.button === 0 &&\n    !evt.metaKey &&\n    !evt.altKey &&\n    !evt.ctrlKey &&\n    !evt.shiftKey\n  );\n}\n\n/**\n * Does an anchor's `target` send this navigation somewhere the router cannot\n * follow? (#1834)\n *\n * `target` names the browsing context the author wants the URL loaded into.\n * Three values are the router's — absent, empty, `_self` — and every other one\n * goes to the browser, the only thing that can resolve a context name.\n * Intercepting instead is how a `<Link target=\"_blank\">` ends up reloading the\n * same tab rather than opening a new one. React Router's\n * `shouldProcessLinkClick` and TanStack Router's `handleClick` split the same\n * way; neither reproduces browsing contexts inside the router, and neither does\n * this.\n *\n * ⚠ The split is by SPELLING, not by where the value resolves to, and three\n * spellings resolve back to this context anyway: `_parent` and `_top` fall back\n * to `_self` in a document with no ancestor, and `_SELF` matches `_self`\n * ASCII-case-insensitively (MDN, `<a>` § target). All three are handed to the\n * browser, which reaches the right destination by a full page load instead of a\n * transition. Resolving them properly means reproducing frame ancestry and\n * keyword folding here; both reference routers decline, and the cost is a page\n * load rather than a wrong destination.\n *\n * ⚠ Ask this only about an `<a>`. On a `<button v-link>` or a `<div use:link>`\n * the attribute is inert markup the browser will not act on, so deferring there\n * would leave the activation unhandled by anyone. Callers that cannot assume an\n * anchor ask {@link anchorTargetsAnotherContext} instead, which narrows first;\n * `target-predicate-authority-1834` owns which call site is in which set.\n */\nexport function targetsAnotherContext(\n  target: string | null | undefined,\n): boolean {\n  return Boolean(target) && target !== \"_self\";\n}\n\n/**\n * The same question, asked about an ELEMENT — for the `use:link` / `v-link`\n * forms, which attach to whatever element the consumer wrote and so cannot\n * assume an anchor (#1834). The `<Link>` components pass a value instead\n * (they render the anchor), and so does Angular's directive (its selector is\n * `a[realLink]`).\n *\n * ⚠ `tagName`, never `instanceof HTMLAnchorElement`, for the reason\n * `applyLinkA11y` sets out below: the constructor belongs to the realm this\n * module loaded in, so a real anchor from an iframe `contentDocument` or a\n * micro-frontend fails the check. Failing it HERE re-intercepts the very\n * `target=\"_blank\"` click this predicate exists to leave alone.\n *\n * Anything that is not an HTML anchor answers `false`: `target` is\n * anchor-specific markup the browser will not act on, so a `<button use:link>`\n * or a `<div v-link>` carrying one must still navigate in-app.\n */\nexport function anchorTargetsAnotherContext(\n  element: Element | null | undefined,\n): boolean {\n  if (element?.tagName !== \"A\") {\n    return false;\n  }\n\n  return targetsAnotherContext(element.getAttribute(\"target\"));\n}\n\n/**\n * RFC 3986 fragment encoding: preserve sub-delims (`&`, `=`, `?`, `:`),\n * encode space, `%`, control chars, non-ASCII via encodeURI; defensively\n * escape `#` (encodeURI does not). Kept BYTE-FOR-BYTE identical to\n * `encodeHashFragment` in `shared/browser-env/url-context.ts` — duplicated\n * because the shared/dom-utils symlink graph does not reach shared/browser-env;\n * a sync test (`link-utils` functional suite) asserts the two stay identical.\n *\n * **STRICTLY-DECODED contract (#1211 / D1=A).** The `<Link hash>` value is a\n * DECODED fragment (no leading `#`) and is encoded verbatim. This OVERTURNS\n * audit-2026-05-17 §5 E.1 — the earlier percent-escape probe (decode + re-encode\n * for copy-from-`location.hash` tolerance) is REMOVED, so both the adapter and\n * the plugin layer obey one contract: `<Link hash=\"a%20b\">` renders `#a%2520b`\n * (the literal fragment `a%20b`) under every runtime. Consumers who want the\n * fragment `a b` pass `hash=\"a b\"`; passing raw `location.hash` (percent-encoded)\n * is no longer supported — it was the source of the plugin↔adapter divergence.\n */\nfunction encodeFragmentInline(decoded: string): string {\n  return encodeURI(decoded).replaceAll(\"#\", \"%23\");\n}\n\ntype BuildUrlFn = (\n  name: string,\n  params: Params,\n  search?: SearchParams,\n  options?: { hash?: string },\n) => string | undefined;\n\n/**\n * Builds an href for a `<Link>` element.\n *\n * - Prefers the URL plugin's `buildUrl` (browser-plugin, navigation-plugin,\n *   hash-plugin) when present.\n * - Falls back to the core's resolving door for runtimes without a URL plugin\n *   (memory-plugin, console UIs, NativeScript). In that fallback the hash\n *   is appended manually so the rendered href is still correct.\n * - The optional 4th argument is the decoded hash fragment (no leading \"#\";\n *   `<Link hash=\"#section\">` is accepted defensively — leading \"#\" stripped),\n *   passed positionally to mirror `navigateWithHash(router, name, params, hash)`\n *   (#1442). Previous 3-arg call sites continue to work unchanged.\n */\nexport function buildHref(\n  router: Router,\n  routeName: string,\n  routeParams: Params,\n  routeSearch?: SearchParams,\n  hash?: string,\n): string | undefined {\n  try {\n    let normHash: string | undefined;\n\n    if (hash !== undefined) {\n      normHash = hash.startsWith(\"#\") ? hash.slice(1) : hash;\n    }\n\n    const buildUrl = router.buildUrl as BuildUrlFn | undefined;\n\n    if (buildUrl) {\n      const url = buildUrl(\n        routeName,\n        routeParams,\n        // Query channel at position 3 (RFC-4 M2 / #1548) — from the `routeSearch`\n        // prop; hash options at position 4. `undefined` when the link has no\n        // `routeSearch` (its query, if any, still rides in routeParams).\n        routeSearch,\n        normHash === undefined ? undefined : { hash: normHash },\n      );\n\n      // Accept only non-empty strings. The BuildUrlFn type contract is\n      // `string | undefined`, but defensive against:\n      //   - `\"\"` (empty string) → would render `<a href=\"\">`, which resolves\n      //     to the current page URL → silent self-navigation on click.\n      //   - `null` (type-contract violation) → would render `<a href={null}>`,\n      //     stringified to `\"null\"` in some renderers.\n      // Either case falls through to the `router.buildPath` fallback below.\n      if (typeof url === \"string\" && url.length > 0) {\n        return url;\n      }\n    }\n\n    // ⚑ RESOLVE, then print (#2250). An href is a promise about where the click\n    // lands, and the click resolves `forwardTo`. `router.buildPath` alone is\n    // class LITERAL by record and answers about the route it was NAMED —\n    // INVARIANTS #8 keeps that so a plugin can build a state for an alias\n    // without being teleported off it — so the chain is resolved FIRST and the\n    // printer prints the target. The door choice belongs here rather than one\n    // layer down.\n    //\n    // ⚠ **The `??` is not reached by a name the table does not hold, and the\n    // claim that it was described a door this arm stopped calling.** That read\n    // `buildNavigationState`, which answers `undefined` for an unknown route;\n    // since #2265 the resolving door is `forwardState`, which ANSWERS for one\n    // (measured — it does not validate the name), leaving the printer to throw.\n    // `packages/react/INVARIANTS.md` row 3 still holds — `Both throw →\n    // undefined + console.error` — but it is reached by both printers throwing,\n    // not by a `undefined` the `??` absorbs. What the `??` IS load-bearing for\n    // is the two cases below.\n    //\n    // ⚠ **The fallback is also where the channel guard lands (#1572).** A route's\n    // declared query name handed in the PATH bag makes the class-① door throw\n    // where `buildPath` answers, so the `??` prints the literal path — the same\n    // href this arm has always rendered. Under a URL plugin the guard is not\n    // swallowed: `router.buildUrl` throws and the outer `catch` drops the href.\n    //\n    // ⚠ **The inner `try` keeps this helper's STRUCTURAL contract.** `buildHref`\n    // is handed a `Router`-shaped object, not necessarily a registered one, and\n    // `getPluginApi` resolves it through `getInternals` — a WeakMap keyed on\n    // identity, which REFUSES a test double or a `Proxy` wrapper. Such a router\n    // keeps the literal path; the stub-router CONTROL in\n    // `packages/react/tests/functional/dom-utils/forwarding-link-href-2250.test.ts`\n    // owns that.\n    let resolved: string | undefined;\n    const api = readPluginApi(router, routeName);\n\n    try {\n      // ⚠ **`forwardState`, not `buildNavigationState`.** Both resolve the whole\n      // chain, and the href is identical — but the committing door opts into\n      // `reportUndeclaredParamKey`, and an href COMMITS NOTHING. That diagnostic\n      // is for a state you are about to persist, which is why `canNavigateTo`\n      // is silent despite sharing `navigate`'s form (#2248 / #1581).\n      // ⚑ **`buildPathResolved`, not `router.buildPath` — ONE href is ONE pass\n      // of the chain (#2260).** The facade's printer runs the `forwardState`\n      // seam a door lower (#2087), which is right for a caller holding a raw\n      // intent and a SECOND pass for this one, which just resolved. Counted,\n      // because nothing in either door's source says \"twice\": the second pass\n      // is what two individually-correct doors compose to.\n      //\n      // ⚠ A plugin author registers one interceptor and has no reason to expect\n      // two invocations per href — a stateful one double-counts. Cost is the\n      // lesser half: +1333 ns per href with `search-schema` +\n      // `persistent-params` installed, per `<Link>` per render.\n      //\n      // ⚠ `api?.` is a TYPE gate, not a runtime guard, and naming it so keeps it\n      // from being read as one: calling `forwardState` on `undefined` throws\n      // into the same `catch` for the same outcome, so a mutant dropping the\n      // check is EQUIVALENT. What it buys is that TypeScript can see the call is\n      // safe below a lookup that may have failed (#2294).\n      const forwarded = api?.forwardState(routeName, routeParams, routeSearch);\n\n      resolved =\n        forwarded &&\n        api?.buildPathResolved(\n          forwarded.name,\n          forwarded.params,\n          forwarded.search,\n        );\n    } catch {\n      resolved = undefined;\n    }\n\n    const path =\n      resolved ?? router.buildPath(routeName, routeParams, routeSearch);\n\n    // Symmetric to the buildUrl guard above (#S1 audit, Invariant 12).\n    // `router.buildPath` is typed `string`, but defends against:\n    //   - `\"\"` (empty string) — would render `<a href=\"\">`, which resolves\n    //     to the current page URL → silent self-navigation on click.\n    //   - non-string type-contract violations from custom path-matchers.\n    // Both yield `undefined` (renderer drops the attribute) with a warning.\n    if (typeof path !== \"string\" || path.length === 0) {\n      console.error(\n        `[real-router] Route \"${routeName}\" yielded an empty path. The element will render without an href attribute.`,\n      );\n\n      return undefined;\n    }\n\n    return normHash ? `${path}#${encodeFragmentInline(normHash)}` : path;\n  } catch {\n    console.error(\n      `[real-router] Route \"${routeName}\" is not defined. The element will render without an href attribute.`,\n    );\n\n    return undefined;\n  }\n}\n\n/**\n * Local extended-options type. Adapters that depend only on `@real-router/core`\n * (without a URL plugin) do not see the `NavigationOptions` augmentation that\n * declares `hash` / `hashChange`. Casting to this widened type inside the\n * helper keeps shared/dom-utils self-contained — adapters do not need to\n * augment NavigationOptions themselves to consume `<Link hash>`.\n */\ntype HashAwareNavigationOptions = NavigationOptions & {\n  hash?: string;\n  hashChange?: boolean;\n};\n\n/**\n * `<Link>` click-handler navigation helper (#532).\n *\n * Wraps `router.navigate(name, params, search, opts)` — the query channel took\n * slot 3 in RFC-4 M2 (#1548) — with same-route different-hash\n * detection: when the consumer clicks a hash-bearing Link that targets the\n * current route with the same params but a different fragment, core's\n * SAME_STATES check would otherwise reject the navigation. The helper adds\n * `force: true` and `hashChange: true` automatically — subscribers can then\n * disambiguate via `state.context.url.hashChanged`.\n *\n * For pure programmatic same-route hash-only navigation, callers are\n * documented to pass `{ force: true }` themselves; the auto-bypass here is\n * a UX convenience for `<Link hash>` that all 6 framework adapters share.\n */\nexport function navigateWithHash(\n  router: Router,\n  routeName: string,\n  routeParams: Params,\n  routeSearch: SearchParams | undefined,\n  hash: string | undefined,\n  extraOptions?: NavigationOptions,\n): Promise<State> {\n  const opts: HashAwareNavigationOptions = { ...extraOptions };\n\n  if (hash !== undefined) {\n    // ⚑ `putField`, not `opts.hash = …` (#2141 / #1852). The spread above produces\n    // no own key for this slot unless the caller's extra options carried one, so\n    // a plain assignment walks the prototype: an ambient accessor an application\n    // or a polyfill put on `Object.prototype` takes the value and the navigation\n    // runs without the fragment it was asked for, or — getter-only — throws.\n    //\n    // ⚠ This dir is shared, so one unguarded write here multiplies by its whole\n    // consumer set — `packages/react` is the coverage and authority owner\n    // (#1838) and owns that count. Its scan classifies COMPUTED-key writes only,\n    // which is why a literal slot like this one sat outside it.\n    putField(opts as unknown as Record<string, unknown>, \"hash\", hash);\n  }\n\n  const current = router.getState();\n\n  // What the navigation gets — the caller's `routeSearch` unless the bypass\n  // below substitutes it (#1925).\n  let navigatedSearch = routeSearch;\n\n  if (current !== undefined) {\n    // ONE expression, asked ONCE and navigated with under the bypass (#1925).\n    // The predicate's answer is only as good as the value it answered ABOUT, so\n    // the two must be the same value — a second copy of this expression would\n    // let them drift apart with nothing to catch it. The name is what keeps\n    // them in agreement structurally rather than by convention.\n    const sameLocationSearch = routeSearch ?? current.search;\n\n    // \"Does this link point at where we already are?\" is a question the router\n    // owns, and asking it here by hand got two things wrong at once (#1555).\n    //\n    // The hand-rolled version compared with `Object.is` per key, so a link\n    // writing `routeSearch={{ page: \"2\" }}` never matched a state parsed from\n    // `?page=2`, where the value is the NUMBER 2 — the bypass silently did not\n    // fire, core rejected the navigation as SAME_STATES, and `<Link hash>`\n    // looked dead. The predicate carries the provenance-tolerant comparison\n    // (#1554) that makes the two forms equal because they print the same URL.\n    //\n    // It also compared the caller's whole `routeParams` bag against\n    // `state.params`, which stopped meaning anything once the channels split\n    // (#1548) — the predicate applies the channel rule instead of re-deriving\n    // it.\n    //\n    // `strictEquality: true` — a link to a PARENT route is a different\n    // location, so the hierarchical arm must not match. `ignoreQueryParams:\n    // false` — the query is part of the location here; ignoring it would fire\n    // the bypass across a real query change and let `force: true` smuggle it\n    // through as a hash change.\n    if (\n      router.isActiveRoute(\n        routeName,\n        routeParams,\n        sameLocationSearch,\n        true,\n        false,\n      )\n    ) {\n      const currentHash =\n        (current.context as { url?: { hash?: string } } | undefined)?.url\n          ?.hash ?? \"\";\n      const newHash = hash ?? currentHash;\n\n      if (currentHash !== newHash) {\n        opts.force = true;\n        opts.hashChange = true;\n\n        // Reaching here means the predicate answered \"this is where we already\n        // are, only the fragment differs\", and `force` + `hashChange` announce\n        // exactly that. The bare `routeSearch` would say \"no query\" rather than\n        // \"unchanged\" — on `/docs?tab=api` a fragment link would land on\n        // `/docs`, moving the location it just announced as unmoved. Same slot,\n        // same conclusion as `scroll-spy.ts`, which re-navigates with both\n        // channels of the CURRENT state.\n        //\n        // ⚠ Only under the bypass. Without it the helper claims no sameness, so\n        // `<Link routeName=\"docs\">` means `/docs` — substituting there would\n        // turn a real navigation into a no-op. An explicit `routeSearch={{}}`\n        // still clears the query, because `{}` is not nullish. Both pinned by\n        // the #1925 suite.\n        navigatedSearch = sameLocationSearch;\n      }\n    }\n  }\n\n  // Query channel at position 3 (RFC-4 M2 / #1548); opts at position 4.\n  return router.navigate(routeName, routeParams, navigatedSearch, opts);\n}\n\n// Match-any-whitespace regex shared across calls. RegExp literals at\n// call-site recompile in some engines; lifting it avoids that microcost\n// for the slow-path branch.\nconst WHITESPACE_PROBE = /\\s/;\nconst WHITESPACE_SPLIT = /\\S+/g;\n\n// `value` is always a truthy class string: both call sites narrow it first\n// (`if (isActive && activeClassName)` / `if (!baseClassName) return …`), so an\n// `if (!value) return []` guard here would be unreachable (#809).\nfunction parseTokens(value: string): string[] {\n  // Hot-path fast-path (audit-2026-05-17 §8b #1): >99% of active-class\n  // inputs at `<Link>` emit are single-token strings like `\"active\"` or\n  // `\"is-current\"` — no whitespace, no leading/trailing pad. Skip the\n  // regex match and Array result allocation: a literal `[value]` works\n  // because the slow-path `match(/\\S+/g)` would return exactly `[value]`\n  // for the same input. PBT lock: linkUtils.properties.ts Invariant 13.\n  if (!WHITESPACE_PROBE.test(value)) {\n    return [value];\n  }\n\n  return value.match(WHITESPACE_SPLIT) ?? [];\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        continue;\n      }\n\n      seen.add(token);\n      baseTokens.push(token);\n    }\n\n    return baseTokens.join(\" \");\n  }\n\n  return baseClassName ?? undefined;\n}\n\n/**\n * One-level structural equality using `Object.is` per key.\n *\n * **String-keyed properties only (Mini-sprint E.3 — audit-5 §4.2 #3).**\n * Implementation walks `Object.keys()` which by spec returns only\n * enumerable own STRING keys. Symbol-keyed properties — created via\n * `obj[Symbol(\"brand\")] = value` or `{ [Symbol(...)]: value }` — are\n * NOT compared. Two records that differ only in a Symbol-keyed value\n * will compare as equal.\n *\n * This is intentional: route params and Link options are documented as\n * string-keyed primitives (string | number | boolean) — Symbol-keyed\n * metadata (e.g. brand markers, private state) doesn't belong in a\n * cache-key comparison. Switching to `Reflect.ownKeys()` would extend\n * the contract to symbols at the cost of one extra allocation per call\n * (Reflect.ownKeys composes string-keys + symbol-keys arrays). If a\n * consumer relies on symbol-keyed metadata for navigation\n * disambiguation, they should encode it into a string key instead.\n *\n * Mirrors React's `shallowEqual` (packages/shared/shallowEqual.js) in\n * both the string-keys-only semantics and the `hasOwnProperty` guard\n * below.\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 = objectKeys(prev);\n  const nextKeys = objectKeys(next);\n\n  if (prevKeys.length !== nextKeys.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    // ⚑ Membership is decided from the LIST the count produced, never from a\n    // second question put to the record (#2064; #1815 settled the same question\n    // for `recordsShallowEqual` in core). `Object.keys` is own AND enumerable\n    // while `hasOwnProperty` is own only — and on a Proxy it is whatever the\n    // `getOwnPropertyDescriptor` trap answers.\n    //\n    // ⚠ `key in next`, `Object.hasOwn` and `propertyIsEnumerable` are the same\n    // family, and none of them is the fix: each leaves a cell of this file's\n    // own suites red. `lint:membership` is the ratchet over the class.\n    //\n    // The second array is free: the count already built it and threw it away.\n    if (\n      !nextKeys.includes(key) ||\n      !Object.is(prevRecord[key], nextRecord[key])\n    ) {\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\n  // Cross-realm safety (audit-2026-05-17 §5 HIGH #4):\n  // `instanceof HTMLAnchorElement` compares against the constructor from\n  // the CURRENT realm. An element created in a different window (iframe\n  // contentDocument, micro-frontend, embedded widget) fails the check\n  // even when it IS a real anchor — the helper would then inject\n  // role=\"link\" + tabindex=\"0\" on top of native anchor semantics,\n  // breaking screen reader output (\"link link\") and focus order.\n  //\n  // tagName is realm-agnostic and is uppercase for HTML-namespaced\n  // elements in any document. SVG `<a>` has lowercase tagName plus a\n  // different prototype (SVGAElement) — skipping it here is wrong by\n  // accident: SVG anchors don't have keyboard activation semantics the\n  // helper would add. But they also don't reach this helper in\n  // practice (router Link components emit HTML anchors). Lock the\n  // uppercase compare to keep the contract narrow.\n  const tag = element.tagName;\n\n  if (tag === \"A\" || tag === \"BUTTON\") {\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 { createActiveSource } from \"@real-router/sources\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { Params, SearchParams } from \"@real-router/core\";\n\nexport function useIsActiveRoute(\n  routeName: string,\n  params?: Params,\n  search?: SearchParams,\n  strict = false,\n  ignoreQueryParams = true,\n  hash?: string,\n): boolean {\n  const router = useRouter();\n\n  // The fast/slow decision — and the `routeName !== \"\"` guard that keeps\n  // `useIsActiveRoute(\"\")` in sync with `router.isActiveRoute(\"\")` (a misused\n  // empty name matches nothing, #1427) — lives in the shared `createActiveSource`\n  // builder, so the adapters built on it resolve active state identically —\n  // Solid is the exception, its `Link` carrying its own copy of the decision\n  // (#1248 landed the fast path inline here; #1427 folded it into the shared\n  // builder). The `useMemo` wrap skips the branch + `canonicalJson(params)` +\n  // cache lookup on every render when all deps (including the `params`\n  // reference) are stable.\n  const store = useMemo(\n    () =>\n      createActiveSource(\n        router,\n        routeName,\n        params,\n        search,\n        strict,\n        ignoreQueryParams,\n        hash,\n      ),\n    [router, routeName, params, search, strict, ignoreQueryParams, hash],\n  );\n\n  return useSyncExternalStore(\n    store.subscribe,\n    store.getSnapshot,\n    store.getSnapshot,\n  );\n}\n","import { createDismissableError } from \"@real-router/sources\";\nimport {\n  useEffect,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useSyncExternalStore,\n} from \"react\";\n\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { ReactElement, ReactNode } 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): ReactElement {\n  const router = useRouter();\n  // Per-router cached in @real-router/sources — the WeakMap lookup is cheap,\n  // but useMemo avoids it entirely on stable-router re-renders (the common\n  // case for boundaries mounted in app shells).\n  const store = useMemo(() => createDismissableError(router), [router]);\n  const snapshot = useSyncExternalStore(\n    store.subscribe,\n    store.getSnapshot,\n    store.getSnapshot,\n  );\n\n  const onErrorRef = useRef(onError);\n\n  // \"Latest ref\" pattern shared with useRouteEnter/useRouteExit: sync the\n  // callback to the ref via useLayoutEffect (synchronous, post-render,\n  // pre-paint) so the snapshot effect below reads the freshest callback\n  // without listing `onError` as a dep — and StrictMode's double-effect\n  // pass writes the same value twice harmlessly.\n  useLayoutEffect(() => {\n    onErrorRef.current = onError;\n  });\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","import { 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","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, primeErrorSource } from \"@real-router/sources\";\nimport { useEffect, useMemo, useSyncExternalStore } from \"react\";\n\nimport { NavigatorContext, RouteContext, RouterContext } from \"./context\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouterProviderCoreProps {\n  router: Router;\n  children: ReactNode;\n}\n\n/**\n * DOM-free provider core: router / route / navigator contexts plus the\n * `useSyncExternalStore` subscription wiring, with **no** dom-utils dependency.\n *\n * Split out of `RouterProvider` (#800) so the terminal `/ink` entry can compose\n * only this: `InkRouterProvider` renders `RouterProviderCore` directly, keeping\n * the scroll-spy / view-transitions / announcer / scroll-restore factories — all\n * structurally unreachable in a terminal — out of the chunk reachable from\n * `dist/esm/ink.mjs`. The DOM-aware `RouterProvider` wraps this core and layers\n * the opt-in DOM-feature effects on top.\n */\nexport const RouterProviderCore: FC<RouterProviderCoreProps> = ({\n  router,\n  children,\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  //\n  // Activity note (#765): that same first-listener/last-listener contract means\n  // a Provider mounted UNDER a React <Activity> / keepAlive boundary drops its\n  // subscription while hidden, so a navigation that lands during the hidden\n  // window is not observed live. createRouteSource RECONCILES on re-subscribe —\n  // when the first listener re-attaches on re-show it re-reads router.getState()\n  // — so the re-shown Provider renders the CURRENT route, not a stale snapshot.\n  // Mounting RouterProvider at the app root (above any Activity boundary) stays\n  // the recommended composition: it keeps the subscription live throughout\n  // instead of relying on the reconnect catch-up. See the \"RouterProvider under\n  // <Activity>\" gotcha in CLAUDE.md and the P1 regression in\n  // tests/integration/reactive-lifecycle.test.tsx.\n  const store = useMemo(() => createRouteSource(router), [router]);\n\n  // #778 P2: eagerly create the per-router error source so a navigation error\n  // that fires BEFORE a RouterErrorBoundary mounts (a lazy app shell, a failed\n  // boot navigation) is still captured. The boundary's createDismissableError\n  // reuses this cached source and catches up (#765); without it the error source\n  // is created lazily on boundary mount — after the error — and never sees it.\n  useEffect(() => {\n    primeErrorSource(router);\n  }, [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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"}