{"version":3,"file":"index.mjs","names":["suspenseComponent","NOOP_INSTANCE","NOOP_INSTANCE","NOOP_INSTANCE","NOOP_INSTANCE","RouterKey","RouteKey","NavigatorKey","useRoute","renderNull","Match","defineComponent","Self","NotFound","MARKER_TYPES","KEEP_ALIVE_VALUES","isSegmentMatch","exact","routeName","fullSegmentName","startsWithSegment","isKeepAliveEnabled","value","collectElements","children","child","result","isVNode","type","Fragment","recordFallback","slots","evaluateMatch","props","segment","nodeName","appendFallback","rendered","UNKNOWN_ROUTE","buildRenderList","activeMatchFound","fallback","hasPerMatchKA","elements","matchProps","useRefFromSource","ref","shallowRef","source","unsub","onScopeDispose","useRouter","router","inject","useRouteNode","snapshot","createRouteNodeSource","getNavigator","computed","getSlotContent","vnode","getOrCreateWrapper","existing","cache","wrapper","markRaw","wrapperCtx","wrapWithSuspense","content","fallbackContent","h","Suspense","emptyKeepAlivePlaceholderInstance","getEmptyKeepAlivePlaceholder","renderWithRootKA","activeChild","WrapperComponent","wrapperCache","slotContent","KeepAlive","renderWithPerMatchKA","RouteViewComponent","routeContext","route","slotOutput","RouteView","EMPTY_PARAMS","EMPTY_OPTIONS","NOOP_INSTANCE$4","createDirectionTracker","popstateFlag","inFlight","replay","hrefOf","buildUrl","hash","state","url","landsOnRouterState","onPopstate","api","getPluginApi","onSettled","unsubs","events","toState","href","ANNOUNCER_ATTR","NOOP_INSTANCE$3","REFS_ATTR","GENERATION_ATTR","readCount","element","writeCount","createRouteAnnouncer","prefix","options","getCustomText","isReady","isDestroyed","lastAnnouncedText","pendingText","clearTimeoutId","announcer","myGeneration","getOrCreateAnnouncer","doAnnounce","text","manageFocus","h1","safariTimeoutId","initialLoadPending","unsubscribe","previousRoute","resolveText","currentGeneration","remaining","removeAnnouncer","nextGeneration","root","next","customText","error","h1Text","objectCreate","NOOP_INSTANCE$2","createScrollRestoration","mode","anchorEnabled","getContainer","behavior","storageKey","store","loadStore","raw","parsed","putPos","cached","key","pos","prevScrollRestoration","readPos","writePos","restoreToken","restorePos","frames","token","attempt","destroyed","top","scrollToHashOrTop","ctxHash","id","scrollSettled","nav","keyOf","onPageHide","current","NOOP_INSTANCE$1","getUrlContext","pickTopmost","bestPositive","bestPositiveDist","bestNegative","bestNegativeDist","entry","entries","zoneTop","distance","createUrlPluginDetector","detectionUnsub","verify","context","onMissing","peekState","detectionConsumed","createCooldown","active","timeout","listenerContainer","listener","clear","lift","createDebouncer","raf","callback","trailingMs","createObserverPair","observed","pending","duplicateIdWarned","mutationTimer","handleIntersection","onIntersection","makeIo","container","observerContainer","io","observeMatches","scope","candidates","selector","onInvalidSelector","seenIds","MUTATION_OBSERVE_INIT","mo","reconcile","nextContainer","createScrollSpy","rootMargin","resolveContainer","silenced","selfEmitting","isStopped","flush","transitionSource","getTransitionSource","detector","cooldown","debouncer","observers","picked","newHash","opts","unsubscribeRouter","NOOP_INSTANCE","createViewTransitions","closeVT","currentVT","successFired","resolveAndClear","offLeave","deferred","resolve","signal","resolveLeave","offSuccess","resolver","scheduledVT","objectKeys","ROUTER_BRAND","isUnreadableRouter","candidate","readPluginApi","resolveLinkTarget","to","routeParams","routeSearch","shouldNavigate","evt","targetsAnotherContext","target","anchorTargetsAnotherContext","encodeFragmentInline","decoded","HOST_PREFIX","buildHref","normHash","resolved","forwarded","path","navigateWithHash","extraOptions","putField","navigatedSearch","sameLocationSearch","currentHash","WHITESPACE_PROBE","WHITESPACE_SPLIT","parseTokens","buildActiveClassName","isActive","activeClassName","activeTokens","baseClassName","baseTokens","seen","shallowEqual","prev","prevKeys","nextKeys","prevRecord","nextRecord","applyLinkA11y","tag","invokeUserOnClick","fn","invokeAttributesOnClick","handlers","Link","cachedParams","stableParams","watch","activeStrict","ignoreQueryParams","createActiveSource","onCleanup","finalClassName","handleClick","attrs","restAttributes","RouterErrorBoundary","createDismissableError","errorVNode","routerStack","pushDirectiveRouter","idx","getDirectiveRouter","isValidBinding","createClickHandler","createKeydownHandler","attachHandlers","click","keydown","detachHandlers","vLink","binding","useNavigator","navigator","useRouteUtils","getRouteUtils","useRouterTransition","useRouteExit","skipSameRoute","isDeactivated","onActivated","onDeactivated","guardedLeave","guardLeaveListener","handler","off","leaveState","useRouteEnter","gate","createRouteEnterGate","newRoute","setupRouteProvision","createRouteSource","initial","createRouterPlugin","releaseDirective","app","watchToggleableUtility","deps","utility","factory","RouterProvider","enabled","anchorScrolling","primeErrorSource","provide"],"sources":["../../src/components/RouteView/components.ts","../../src/components/RouteView/helpers.ts","../../src/useRefFromSource.ts","../../src/composables/useRouter.ts","../../src/composables/useRouteNode.ts","../../src/components/RouteView/RouteView.ts","../../src/constants.ts","../../../../shared/dom-utils/direction-tracker.ts","../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/scroll-spy.ts","../../../../shared/dom-utils/view-transitions.ts","../../../../shared/dom-utils/link-utils.ts","../../src/components/Link.ts","../../src/components/RouterErrorBoundary.ts","../../src/directives/vLink.ts","../../src/composables/useNavigator.ts","../../src/composables/useRouteUtils.ts","../../src/composables/useRouterTransition.ts","../../src/composables/useRouteExit.ts","../../src/composables/useRouteEnter.ts","../../src/setupRouteProvision.ts","../../src/createRouterPlugin.ts","../../src/RouterProvider.ts"],"sourcesContent":["import { defineComponent } from \"vue\";\n\nimport type { SelfProps } from \"./types\";\nimport type { FunctionalComponent, PropType, VNode } from \"vue\";\n\nfunction renderNull() {\n  return null;\n}\n\nexport const Match = defineComponent({\n  name: \"RouteView.Match\",\n  props: {\n    segment: {\n      type: String as PropType<string>,\n      required: true,\n    },\n    exact: {\n      type: Boolean,\n      default: false,\n    },\n    fallback: {\n      type: [Object, Function] as PropType<VNode | (() => VNode)>,\n      default: undefined,\n    },\n    keepAlive: {\n      type: Boolean,\n      default: false,\n    },\n  },\n  render: renderNull,\n});\n\n// Type Self via FunctionalComponent<SelfProps> so the SelfProps interface\n// is anchored to the component contract — knip otherwise flags SelfProps\n// as unused even though it's re-exported as RouteViewSelfProps for\n// consumers wrapping Self in custom HOCs.\nconst SelfImpl = defineComponent({\n  name: \"RouteView.Self\",\n  props: {\n    fallback: {\n      type: [Object, Function] as PropType<VNode | (() => VNode)>,\n      default: undefined,\n    },\n  },\n  render: renderNull,\n});\n\nexport const Self = SelfImpl as unknown as FunctionalComponent<SelfProps>;\n\nexport const NotFound = defineComponent({\n  name: \"RouteView.NotFound\",\n  render: renderNull,\n});\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Fragment, isVNode } from \"vue\";\n\nimport { Match, NotFound, Self } from \"./components\";\n\nimport type { VNode } from \"vue\";\n\nconst MARKER_TYPES: ReadonlySet<unknown> = new Set([Match, Self, NotFound]);\nconst KEEP_ALIVE_VALUES: ReadonlySet<unknown> = new Set([\n  true,\n  \"\",\n  \"keep-alive\",\n]);\n\ntype FallbackType = VNode | (() => VNode) | undefined;\n\ninterface FallbackSlots {\n  selfVNode: VNode | null;\n  selfFallback: FallbackType;\n  notFoundVNode: VNode | null;\n}\n\nexport function 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\n// Vue compiles boolean-shorthand template attributes (`<Match keepAlive>`) to\n// an empty string instead of `true`, and converts them to `true` only when the\n// receiving component's prop is declared with `type: Boolean`. `Match` is a\n// marker component (`render: null`) — its props are inspected on the VNode\n// without ever going through Vue's prop-casting pipeline, so the raw `\"\"` (or\n// the hyphenated attribute name) reaches us here. Accept the same trio Vue's\n// runtime does.\nexport function isKeepAliveEnabled(value: unknown): boolean {\n  return KEEP_ALIVE_VALUES.has(value);\n}\n\n// Accumulates into the caller's array rather than returning one per nesting\n// level and spreading it in: the spread passed one argument per collected\n// VNode, and V8 caps spread arguments by remaining stack, so a wide enough\n// nested fragment threw `RangeError: Maximum call stack size exceeded`\n// (`route-view-slot-scale.stress.ts` owns the width). Accumulating also drops\n// the intermediate array per level, on a path that runs on every RouteView\n// render. `react` and `preact` write this same walk as an accumulator; only\n// react is also allocation-free, since preact's `toChildArray` builds one.\nexport function collectElements(children: unknown, result: VNode[]): void {\n  if (Array.isArray(children)) {\n    for (const child of children) {\n      collectElements(child, result);\n    }\n  } else if (isVNode(children)) {\n    // Read once: `children` is the caller's VNode, and asking it for `type`\n    // twice is the #2085 class — `read-count-authority` derives that site set\n    // across every package, so a second read here reds a core test.\n    const { type } = children;\n\n    if (MARKER_TYPES.has(type)) {\n      result.push(children);\n    } else if (type === Fragment) {\n      collectElements(children.children, result);\n    }\n  }\n}\n\nfunction recordFallback(child: VNode, slots: FallbackSlots): boolean {\n  if (child.type === NotFound) {\n    // First-wins: store the FIRST NotFound VNode, ignore later ones — symmetric\n    // with <Self> below and the React/Preact/Solid adapters (#1439). Store the\n    // VNode (never null for a real marker), not `child.children` (null for a\n    // childless <NotFound/>), so the assign-once guard cannot be defeated.\n    slots.notFoundVNode ??= child;\n\n    return true;\n  }\n\n  if (child.type === Self) {\n    if (slots.selfVNode === null) {\n      slots.selfVNode = child;\n      const props = child.props as { fallback?: FallbackType } | null;\n\n      slots.selfFallback = props?.fallback;\n    }\n\n    return true;\n  }\n\n  return false;\n}\n\nfunction evaluateMatch(\n  child: VNode,\n  routeName: string,\n  nodeName: string,\n): { isActive: boolean; fallback: FallbackType } {\n  const props = child.props as {\n    segment: string;\n    exact?: boolean;\n    fallback?: FallbackType;\n  } | null;\n  const segment = props?.segment ?? \"\";\n  const exact = props?.exact ?? false;\n  const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n  const isActive = isSegmentMatch(routeName, fullSegmentName, exact);\n\n  return { isActive, fallback: props?.fallback };\n}\n\nfunction appendFallback(\n  rendered: VNode[],\n  routeName: string,\n  nodeName: string,\n  slots: FallbackSlots,\n): FallbackType {\n  if (slots.selfVNode !== null && routeName === nodeName) {\n    rendered.push(slots.selfVNode);\n\n    return slots.selfFallback;\n  }\n\n  if (routeName === UNKNOWN_ROUTE && slots.notFoundVNode !== null) {\n    rendered.push(slots.notFoundVNode);\n  }\n\n  return undefined;\n}\n\nexport function buildRenderList(\n  elements: VNode[],\n  routeName: string,\n  nodeName: string,\n): {\n  rendered: VNode[];\n  activeMatchFound: boolean;\n  fallback?: FallbackType;\n  /**\n   * True iff any `<Match>` child in the input has its `keepAlive` prop set\n   * to one of Vue's accepted boolean-shorthand forms. Surfaced as a\n   * side-channel from the single pipeline pass so the caller doesn't have\n   * to re-iterate `elements` after `buildRenderList` returns — closes a MED\n   * code-quality finding (audit §8.1).\n   */\n  hasPerMatchKA: boolean;\n} {\n  const slots: FallbackSlots = {\n    selfVNode: null,\n    selfFallback: undefined,\n    notFoundVNode: null,\n  };\n  let activeMatchFound = false;\n  let fallback: FallbackType;\n  let hasPerMatchKA = false;\n  const rendered: VNode[] = [];\n\n  for (const child of elements) {\n    // Match-only side-channel: scan for the keepAlive shorthand in the same\n    // pass that already inspects every child. Short-circuits once a positive\n    // is found to avoid redundant prop reads in big slot trees.\n    if (!hasPerMatchKA && child.type === Match) {\n      const matchProps = child.props as { keepAlive?: unknown } | null;\n\n      if (isKeepAliveEnabled(matchProps?.keepAlive)) {\n        hasPerMatchKA = true;\n      }\n    }\n\n    if (recordFallback(child, slots)) {\n      continue;\n    }\n\n    if (activeMatchFound) {\n      continue;\n    }\n\n    const result = evaluateMatch(child, routeName, nodeName);\n\n    if (result.isActive) {\n      activeMatchFound = true;\n      fallback = result.fallback;\n      rendered.push(child);\n    }\n  }\n\n  if (!activeMatchFound) {\n    fallback = appendFallback(rendered, routeName, nodeName, slots);\n  }\n\n  return { rendered, activeMatchFound, fallback, hasPerMatchKA };\n}\n","import { shallowRef, onScopeDispose } from \"vue\";\n\nimport type { RouterSource } from \"@real-router/sources\";\nimport type { ShallowRef } from \"vue\";\n\nexport function useRefFromSource<T>(\n  // Only `subscribe` + `getSnapshot` are consumed — teardown goes through the\n  // `subscribe` unsub via `onScopeDispose`, never `source.destroy`. Narrowing to\n  // this shape lets the #1250 name-selector fast path pass a 2-method wrapper\n  // (no dead `destroy` needed); full `RouterSource`s still satisfy it.\n  source: Pick<RouterSource<T>, \"subscribe\" | \"getSnapshot\">,\n): ShallowRef<T> {\n  const ref = shallowRef(source.getSnapshot());\n\n  const unsub = source.subscribe(() => {\n    ref.value = source.getSnapshot();\n  });\n\n  onScopeDispose(unsub);\n\n  return ref;\n}\n","import { inject } from \"vue\";\n\nimport { RouterKey } from \"../context\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport const useRouter = (): Router => {\n  const router = inject(RouterKey);\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 { computed } from \"vue\";\n\nimport { useRefFromSource } from \"../useRefFromSource\";\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouteContext } from \"../types\";\n\nexport function useRouteNode(nodeName: string): RouteContext {\n  const router = useRouter();\n\n  const source = createRouteNodeSource(router, nodeName);\n  const snapshot = useRefFromSource(source);\n\n  // getNavigator is WeakMap-cached in core; no useMemo equivalent needed.\n  const navigator = getNavigator(router);\n\n  // Derive route/previousRoute via computed instead of mirroring with a sync\n  // watch into two extra shallowRefs. computed shares snapshot's identity so\n  // when the underlying source emits the same reference (idempotent or\n  // out-of-node nav), consumers don't see a new ref.\n  const route = computed(() => snapshot.value.route);\n  const previousRoute = computed(() => snapshot.value.previousRoute);\n\n  return {\n    navigator,\n    route,\n    previousRoute,\n  };\n}\n","import {\n  Fragment,\n  defineComponent,\n  h,\n  KeepAlive,\n  markRaw,\n  Suspense,\n} from \"vue\";\n\nimport { Match, NotFound, Self } from \"./components\";\nimport {\n  buildRenderList,\n  collectElements,\n  isKeepAliveEnabled,\n} from \"./helpers\";\nimport { useRouteNode } from \"../../composables/useRouteNode\";\n\nimport type { Component, VNode } from \"vue\";\n\ntype SlotChildren = Record<string, (() => VNode[]) | undefined> | null;\n\nfunction getSlotContent(vnode: VNode): VNode[] | null {\n  const slots = vnode.children as SlotChildren;\n\n  return slots?.default?.() ?? null;\n}\n\nfunction getOrCreateWrapper(\n  cache: Map<string, Component>,\n  segment: string,\n): Component {\n  const existing = cache.get(segment);\n\n  if (existing) {\n    return existing;\n  }\n\n  const wrapper = markRaw(\n    defineComponent({\n      name: `KeepAlive-${segment}`,\n      setup(_wrapperProps, wrapperCtx) {\n        return () => wrapperCtx.slots.default?.();\n      },\n    }),\n  );\n\n  cache.set(segment, wrapper);\n\n  return wrapper;\n}\n\nfunction wrapWithSuspense(content: VNode, fallback: unknown): VNode {\n  if (fallback === undefined) {\n    return content;\n  }\n\n  const fallbackContent =\n    typeof fallback === \"function\" ? (fallback as () => VNode)() : fallback;\n\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment\n  const suspenseComponent = Suspense as any;\n\n  return h(\n    suspenseComponent,\n    {},\n    {\n      default: () => content,\n      fallback: () => fallbackContent,\n    },\n  );\n}\n\n// Lazy-initialised — only allocated when a per-Match keepAlive path needs to\n// keep the cache slot \"occupied\" with a no-render placeholder. Apps without\n// keepAlive never pay the markRaw + defineComponent allocation at import.\nlet emptyKeepAlivePlaceholderInstance: Component | null = null;\n\nfunction getEmptyKeepAlivePlaceholder(): Component {\n  emptyKeepAlivePlaceholderInstance ??= markRaw(\n    defineComponent({\n      name: \"KeepAlive-placeholder\",\n      render() {\n        return null;\n      },\n    }),\n  );\n\n  return emptyKeepAlivePlaceholderInstance;\n}\n\nfunction renderWithRootKA(\n  activeChild: VNode,\n  wrapperCache: Map<string, Component>,\n  fallback: unknown,\n): VNode {\n  const activeProps = activeChild.props as { segment?: string } | null;\n  const segment = activeProps?.segment ?? \"__not-found__\";\n  const WrapperComponent = getOrCreateWrapper(wrapperCache, segment);\n  const slotContent = getSlotContent(activeChild) ?? [];\n  const keepAliveContent = h(KeepAlive, null, {\n    default: () =>\n      h(WrapperComponent, { key: segment }, { default: () => slotContent }),\n  });\n\n  return wrapWithSuspense(keepAliveContent, fallback);\n}\n\nfunction renderWithPerMatchKA(\n  activeChild: VNode,\n  wrapperCache: Map<string, Component>,\n  fallback: unknown,\n): VNode | null {\n  const matchProps = activeChild.props as {\n    segment?: string;\n    keepAlive?: unknown;\n  } | null;\n\n  if (isKeepAliveEnabled(matchProps?.keepAlive) && activeChild.type === Match) {\n    /* v8 ignore start */\n    const segment = matchProps?.segment ?? \"__not-found__\";\n    /* v8 ignore stop */\n    const WrapperComponent = getOrCreateWrapper(wrapperCache, segment);\n    const slotContent = getSlotContent(activeChild) ?? [];\n\n    return h(Fragment, [\n      h(KeepAlive, null, {\n        default: () =>\n          h(WrapperComponent, { key: segment }, { default: () => slotContent }),\n      }),\n    ]);\n  }\n\n  const content = getSlotContent(activeChild);\n\n  /* v8 ignore start */\n  if (!content) {\n    return null;\n  }\n  /* v8 ignore stop */\n\n  return h(Fragment, [\n    h(KeepAlive, null, {\n      default: () => h(getEmptyKeepAlivePlaceholder()),\n    }),\n    wrapWithSuspense(h(Fragment, content), fallback),\n  ]);\n}\n\nconst RouteViewComponent = defineComponent({\n  name: \"RouteView\",\n  props: {\n    nodeName: {\n      type: String,\n      required: true,\n    },\n    keepAlive: {\n      type: Boolean,\n      default: false,\n    },\n  },\n  setup(props, { slots }) {\n    const routeContext = useRouteNode(props.nodeName);\n    const wrapperCache = new Map<string, Component>();\n\n    return (): VNode | null => {\n      const route = routeContext.route.value;\n\n      if (!route) {\n        return null;\n      }\n\n      const slotOutput = slots.default?.();\n      const elements: VNode[] = [];\n\n      collectElements(slotOutput, elements);\n\n      // `hasPerMatchKA` is a side-channel produced by the same pipeline pass\n      // that builds `rendered` — closes the audit §8.1 \"double iteration\"\n      // finding. The previous identity-cache on `slotOutput` is no longer\n      // needed: per-render cost is one O(n) walk instead of two.\n      const { rendered, fallback, hasPerMatchKA } = buildRenderList(\n        elements,\n        route.name,\n        props.nodeName,\n      );\n\n      if (rendered.length === 0) {\n        return null;\n      }\n\n      const activeChild = rendered[0];\n\n      if (props.keepAlive) {\n        return renderWithRootKA(activeChild, wrapperCache, fallback);\n      }\n\n      /* v8 ignore start */\n      if (\n        activeChild.type !== Match &&\n        activeChild.type !== Self &&\n        activeChild.type !== NotFound\n      ) {\n        return null;\n      }\n      /* v8 ignore stop */\n\n      if (hasPerMatchKA) {\n        return renderWithPerMatchKA(activeChild, wrapperCache, fallback);\n      }\n\n      const content = getSlotContent(activeChild);\n\n      if (!content) {\n        return null;\n      }\n\n      return wrapWithSuspense(h(Fragment, content), fallback);\n    };\n  },\n});\n\nexport const RouteView = Object.assign(RouteViewComponent, {\n  Match,\n  Self,\n  NotFound,\n});\n\nexport type {\n  RouteViewProps,\n  MatchProps as RouteViewMatchProps,\n  SelfProps as RouteViewSelfProps,\n  NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\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 { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport type { Params, Router, SearchParams, State } from \"@real-router/core\";\n\ntype BuildUrl = (\n  name: string,\n  params: Params,\n  search?: SearchParams,\n  options?: { hash?: string },\n) => string | undefined;\n\nexport interface DirectionTracker {\n  destroy: () => void;\n}\n\nconst NOOP_INSTANCE: DirectionTracker = Object.freeze({\n  destroy: () => {\n    /* no-op */\n  },\n});\n\n/**\n * Track navigation direction (forward / back) and write it to\n * `<html data-nav-direction>` on every leave. CSS / JS readers consume\n * the attribute via `html[data-nav-direction=\"back\"]` selectors or\n * `document.documentElement.dataset.navDirection`.\n *\n * Mechanism-agnostic — works identically whether downstream UI uses CSS\n * `@keyframes`, View Transitions pseudo-elements, or library state\n * (motion's `motion.div initial={{ x: ... }}`).\n *\n * Implementation:\n *   - On install, set `data-nav-direction=\"forward\"` baseline.\n *   - Attach a `popstate` listener that flips an internal flag to\n *     `true`. Browser back/forward navigation triggers popstate; user\n *     clicks on `<Link>` / programmatic `router.navigate(...)` do not.\n *   - On every `subscribeLeave`, write\n *     `popstateFlag ? \"back\" : \"forward\"` and reset the flag.\n *   - A `popstate` that arrives while a transition is in flight arms the\n *     flag of the transition after it, which is the one a URL plugin starts\n *     for it.\n *\n * Returns `{ destroy }` to clean up the listener and clear the dataset\n * attribute.\n */\nexport function createDirectionTracker(router: Router): DirectionTracker {\n  if (typeof document === \"undefined\") {\n    return NOOP_INSTANCE;\n  }\n\n  let popstateFlag = false;\n\n  document.documentElement.dataset.navDirection = \"forward\";\n\n  // The `toState` the transition in flight was announced with.\n  let inFlight: State | undefined;\n  // The address of a popstate that arrived while a transition was in flight.\n  // A URL plugin starts its traversal after that transition, so its flag is\n  // kept apart from the one the transition in flight may own.\n  let replay: string | undefined;\n\n  // ⚑ The flag is SPENT by the transition it belongs to, whether or not that\n  // transition reaches a leave phase (#1924). It is armed by any `popstate` on\n  // `globalThis`, not only one the router consumes, so `history.back()` onto an\n  // entry that resolves to the current state — core answers `SAME_STATES`, and\n  // measured, that arc emits `TRANSITION_ERROR` with no `TRANSITION_START` and\n  // no leave — would otherwise leave it armed indefinitely and publish the next\n  // FORWARD navigation as \"back\".\n  //\n  // ⚠ A clock has no correct value to be set to. The leave phase is separated\n  // from its popstate by every deactivation guard, and a guard may await\n  // anything; the plugin also DEFERS a popstate that arrives during an\n  // in-flight transition and replays it from that transition's `finally`. The\n  // gap has no upper bound. Measured on the obvious choice: a task-boundary\n  // expiry disarms a live navigation on ONE async `canDeactivate`.\n\n  // The address the URL plugin prints a state at, or `undefined` without\n  // one, or when its `buildUrl` throws or answers none.\n  const hrefOf = (state: State): string | undefined => {\n    const buildUrl = router.buildUrl as BuildUrl | undefined;\n\n    if (buildUrl === undefined) {\n      return undefined;\n    }\n\n    try {\n      const hash = (state.context as { url?: { hash?: string } } | undefined)\n        ?.url?.hash;\n      const url = buildUrl(\n        state.name,\n        state.params,\n        state.search,\n        hash ? { hash } : undefined,\n      );\n\n      return url === undefined\n        ? undefined\n        : new URL(url, globalThis.location.href).href;\n    } catch {\n      return undefined;\n    }\n  };\n\n  // ⚑ A traversal onto the URL of the router's own state starts no\n  // transition: a URL plugin's return after a refused Back or Forward\n  // (#2740), or a Back onto an entry holding that state. The URL comes from\n  // the URL plugin's `buildUrl`; without one, every popstate arms the flag.\n  const landsOnRouterState = (): boolean => {\n    const state = router.getState();\n\n    return state !== undefined && hrefOf(state) === globalThis.location.href;\n  };\n\n  const onPopstate = (): void => {\n    if (landsOnRouterState()) {\n      // The URL plugin returns after a traversal it refused or one a guard\n      // cancelled quietly, and drops the popstate that waited behind it, so\n      // the flag that popstate's replay would have spent ends here.\n      if (inFlight === undefined) {\n        popstateFlag = false;\n      }\n\n      return;\n    }\n\n    if (inFlight === undefined) {\n      popstateFlag = true;\n    } else {\n      replay = globalThis.location.href;\n    }\n  };\n\n  // IMPORTANT — listener-ordering: `popstate` fires on `window`, which\n  // has no DOM descendants, so capture phase is moot. Listeners are\n  // dispatched in registration order. To beat the browser-plugin's own\n  // popstate handler, this tracker must be installed **before**\n  // `router.usePlugin(browserPluginFactory())` in user code. Otherwise\n  // the plugin's handler runs first and synchronously fires\n  // `subscribeLeave` while `popstateFlag` is still `false`.\n  globalThis.addEventListener(\"popstate\", onPopstate);\n\n  const api = getPluginApi(router);\n\n  // The flag of a popstate that arrived during the transition passes to the\n  // next one; the transition's own, unspent, ends with it.\n  const onSettled = (): void => {\n    inFlight = undefined;\n    popstateFlag = replay !== undefined;\n    replay = undefined;\n  };\n\n  // Core also reports a refusal that starts no transition on\n  // `TRANSITION_ERROR`, and may do so while another transition runs. A\n  // transition's own error carries a `toState` with that transition's\n  // `context`, so another one does not settle it (#2779). With nothing in\n  // flight every error settles: the `SAME_STATES` arc above spends the flag\n  // that way.\n  const onError = (toState: State | undefined): void => {\n    if (inFlight === undefined || toState?.context === inFlight.context) {\n      onSettled();\n\n      return;\n    }\n\n    // A popstate that arrived while the transition runs may be the one this\n    // refusal answers: a URL plugin handles a popstate that arrives during a\n    // programmatic transition on the spot. It is, unless the refusal names\n    // another address.\n    const href = toState === undefined ? undefined : hrefOf(toState);\n\n    if (href === undefined || href === replay) {\n      replay = undefined;\n    }\n  };\n\n  const unsubs = [\n    api.addEventListener(events.TRANSITION_START, (toState: State) => {\n      inFlight = toState;\n    }),\n    api.addEventListener(events.TRANSITION_SUCCESS, onSettled),\n    api.addEventListener(events.TRANSITION_ERROR, onError),\n    api.addEventListener(events.TRANSITION_CANCEL, onSettled),\n    router.subscribeLeave(() => {\n      document.documentElement.dataset.navDirection = popstateFlag\n        ? \"back\"\n        : \"forward\";\n      popstateFlag = false;\n    }),\n  ];\n\n  return {\n    destroy: () => {\n      for (const unsub of unsubs) {\n        unsub();\n      }\n\n      globalThis.removeEventListener(\"popstate\", onPopstate);\n      delete document.documentElement.dataset.navDirection;\n    },\n  };\n}\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\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n  destroy: () => {\n    /* no-op */\n  },\n});\n\n// Live (non-NOOP) instances sharing the single `[data-real-router-announcer]`\n// aria-live element. The element is created once by the first instance\n// (`getOrCreateAnnouncer`) and reused by the rest; it must be removed only when\n// the LAST holder is destroyed. Without this count the first provider's\n// destroy() would detach the shared node while sibling providers (micro-\n// frontends — the same multi-provider scenario `scroll-restore`'s `storageKey`\n// exists for) keep writing to the now-orphaned node → silent screen reader (#783).\n// ⚑ The ref-count and the generation live ON THE ELEMENT (#1924). The element\n// is found with `document.querySelector`, so it is document-scoped, while a\n// module variable is bundle-scoped — and a page carrying a second adapter\n// bundle, the micro-frontend case the ref-count exists for, shares the element\n// and not the scope.\n//\n// ⚠ Module-scoped counters reach #783 verbatim through that seam: the second\n// bundle takes the `existing` branch, reads its own generation, passes the\n// #1217 ownership guard, decrements its own count to zero and removes the live\n// element.\nconst REFS_ATTR = \"data-rr-announcer-refs\";\nconst GENERATION_ATTR = \"data-rr-announcer-generation\";\n\nfunction readCount(element: HTMLElement): number {\n  // `Number(null)` is 0, so an absent attribute needs no fallback term.\n  return Number(element.getAttribute(REFS_ATTR));\n}\n\nfunction writeCount(element: HTMLElement, value: number): void {\n  element.setAttribute(REFS_ATTR, String(value));\n}\n// Generation token (#1217): bumped each time a FRESH shared element is created.\n// Each instance captures the generation live at construction; on destroy it\n// touches the shared refcount / element ONLY if its generation is still current.\n// A stale instance — whose element a host wiped without calling destroy() — must\n// not decrement the new generation's refcount (→ negative) or remove its live\n// element (a selector-based removeAnnouncer takes whoever is in the DOM).\n\nexport function createRouteAnnouncer(\n  router: Router,\n  options?: RouteAnnouncerOptions,\n): { destroy: () => void } {\n  // Defensive SSR / non-browser guard: in SSR (Node.js) or non-DOM\n  // environments, `document` is undefined and the announcer cannot\n  // attach its aria-live region. Return a frozen NOOP_INSTANCE — same\n  // pattern as `createDirectionTracker`, `createScrollRestoration`, and\n  // `createViewTransitions`. Without this guard, `NavigationAnnouncer`\n  // component construction would throw `ReferenceError: document is not\n  // defined` under `@angular/ssr` rendering, tearing down the whole SSR\n  // bootstrap. Closes review-2026-05-10 §5.10 ⛔ \"NavigationAnnouncer\n  // SSR mode\" MED.\n  if (typeof document === \"undefined\") {\n    return NOOP_INSTANCE;\n  }\n\n  const prefix = options?.prefix ?? \"Navigated to \";\n  const getCustomText = options?.getAnnouncementText;\n\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 { element: announcer, generation: myGeneration } =\n    getOrCreateAnnouncer();\n\n  writeCount(announcer, readCount(announcer) + 1);\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  // The initial page load is the commit with no previous route — `start()`'s\n  // — of a router that shows no state when the announcer is created. The\n  // canonical mount order creates it after that commit; a `start()` after\n  // `stop()` changes the page with no load, and is announced.\n  let initialLoadPending = router.getState() === undefined;\n\n  const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n    if (previousRoute === undefined && initialLoadPending) {\n      initialLoadPending = 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      // Idempotency guard — required so the ref-count is decremented EXACTLY\n      // once per instance. A double destroy() must not drop the count below the\n      // number of live holders (which would detach a sibling's element, or\n      // leave it attached forever).\n      if (isDestroyed) {\n        return;\n      }\n\n      isDestroyed = true;\n      unsubscribe();\n      clearTimeout(clearTimeoutId);\n      clearTimeout(safariTimeoutId);\n\n      // Ownership guard (#1217): if a host wiped the shared element out from\n      // under us, the next getOrCreateAnnouncer bumped the generation for the\n      // fresh element. A stale instance must NOT decrement the new generation's\n      // refcount (→ negative) or remove its live element — bail if not current.\n      // ⚠ Against the DOCUMENT's generation, never the captured element's: the\n      // element a stale instance holds was removed, and its own attribute\n      // still reads its own generation, so comparing the two can only ever be\n      // equal and the guard would be dead.\n      if (myGeneration !== currentGeneration()) {\n        return;\n      }\n\n      const remaining = readCount(announcer) - 1;\n\n      writeCount(announcer, remaining);\n\n      // Only the last holder tears down the shared element — via our captured\n      // ref, not a selector query (which would delete whoever's element is\n      // currently in the DOM, i.e. a newer generation's).\n      if (remaining === 0) {\n        removeAnnouncer(announcer);\n      }\n    },\n  };\n}\n\n/**\n * The next generation, derived from the document rather than a module counter:\n * a wiped element leaves no node to read, so the value is kept on `<html>`.\n */\nfunction currentGeneration(): number {\n  return Number(document.documentElement.getAttribute(GENERATION_ATTR));\n}\n\nfunction nextGeneration(): number {\n  const root = document.documentElement;\n  const next = currentGeneration() + 1;\n\n  root.setAttribute(GENERATION_ATTR, String(next));\n\n  return next;\n}\n\nfunction getOrCreateAnnouncer(): {\n  element: HTMLElement;\n  generation: number;\n} {\n  const existing = document.querySelector<HTMLElement>(`[${ANNOUNCER_ATTR}]`);\n\n  if (existing) {\n    return {\n      element: existing,\n      generation: Number(existing.getAttribute(GENERATION_ATTR)),\n    };\n  }\n\n  // Creating a FRESH element means no live instance is validly sharing one, so\n  // the ref-count restarts from zero (the caller increments immediately after).\n  // Without this, an element removed out from under live instances — a host\n  // wiping the subtree, or a consumer test whose teardown clears the DOM\n  // without calling every instance's destroy() — would leave a stale positive\n  // count that prevents the new element from ever being torn down (#783).\n  // The generation bump (#1217) lets the wiped element's instances recognize\n  // themselves as stale so their destroy() does not touch this fresh element.\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  // A fresh element restarts the count at zero (the caller increments straight\n  // after) and takes the next generation, so instances holding the element a\n  // host wiped recognise themselves as stale. Both live on the node, so every\n  // bundle reads the same values.\n  writeCount(element, 0);\n  element.setAttribute(GENERATION_ATTR, String(nextGeneration()));\n\n  // Defensive SSR / pre-`<body>` guard: in some environments (early\n  // injection, deferred-body documents, certain SSR rehydration paths)\n  // `document.body` can be null when the announcer is constructed.\n  // `document.body.prepend(...)` would throw `TypeError: Cannot read\n  // properties of null`, tearing down the consumer's RouterProvider /\n  // NavigationAnnouncer mount. Fallback to `documentElement` keeps the\n  // announcer working for SR users; visual-hidden styling means there is\n  // no visible artifact regardless of mount point.\n  //\n  // TS dom lib types `document.body` as `HTMLElement` (non-null), but\n  // runtime can return null per spec. The `as` cast narrows the type to\n  // include null so the `??` short-circuit is type-safe.\n  ((document.body as HTMLElement | null) ?? document.documentElement).prepend(\n    element,\n  );\n\n  return {\n    element,\n    generation: Number(element.getAttribute(GENERATION_ATTR)),\n  };\n}\n\nfunction removeAnnouncer(element: HTMLElement): void {\n  element.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    try {\n      const customText = getCustomText(route);\n\n      // Mini-sprint E.4 (audit-5 §4.2 #4) — empty-string fallback.\n      // A consumer pattern like\n      //   getAnnouncementText: (route) => myMap[route.name] ?? \"\"\n      // returns `\"\"` for routes outside the map. The subscribe loop\n      // then sees an empty text and silently no-announces — screen\n      // readers stay quiet without any signal to the developer. Treat\n      // a falsy custom result (`\"\"` / `null` / `undefined`) as\n      // \"consumer doesn't have a name for this route\" and fall through\n      // to the default resolution chain (h1 → title → route name).\n      if (customText) {\n        return customText;\n      }\n    } catch (error) {\n      // A throwing consumer callback inside the router's subscribe loop\n      // would tear down sibling listeners — log and fall through to the\n      // built-in resolution chain so the announcer keeps working.\n      console.error(\n        \"[real-router] getAnnouncementText threw; falling back to default resolution.\",\n        error,\n      );\n    }\n  }\n\n  const h1Text = (h1?.textContent ?? \"\").trim();\n  const routeName = route.name.startsWith(INTERNAL_ROUTE_PREFIX)\n    ? \"\"\n    : route.name;\n  const rawText =\n    h1Text || document.title || routeName || globalThis.location.pathname;\n\n  return `${prefix}${rawText}`;\n}\n\nfunction manageFocus(h1: HTMLElement | null): void {\n  if (!h1) {\n    return;\n  }\n\n  if (!h1.hasAttribute(\"tabindex\")) {\n    h1.setAttribute(\"tabindex\", \"-1\");\n  }\n\n  h1.focus({ preventScroll: true });\n}\n","import type { Router, State } from \"@real-router/core\";\n\n/** Captured like the deciding seven, but this one BUILDS the guarantee (#2072). */\nconst objectCreate = Object.create;\n\nconst DEFAULT_STORAGE_KEY = \"real-router:scroll\";\n\n// Bounded retry budget for resolving a late-mounting scroll container on the\n// restore path. A per-route container (e.g. an `overflow:auto` div rendered\n// only on one route) can be committed to the DOM a few frames after the\n// navigation settles — heavier routes paint later than the subscribe's rAF.\n// ~10 frames (≈160ms at 60fps) comfortably covers a React commit of a large\n// route without being perceptible. See the doc-block on `restorePos`.\nconst RESTORE_RETRY_FRAMES = 10;\n\nconst NOOP_INSTANCE: { destroy: () => void } = Object.freeze({\n  destroy: () => {\n    /* no-op */\n  },\n});\n\nexport type ScrollRestorationMode = \"restore\" | \"top\" | \"native\";\n\nexport interface ScrollRestorationOptions {\n  mode?: ScrollRestorationMode | undefined;\n  anchorScrolling?: boolean | undefined;\n  scrollContainer?: (() => HTMLElement | null) | undefined;\n  /**\n   * Scroll behavior passed to `scrollTo({ behavior })` and\n   * `scrollIntoView({ behavior })`.\n   *\n   * - `\"auto\"` (default) — browser-defined, usually instant.\n   * - `\"instant\"` — explicit instant jump (no animation).\n   * - `\"smooth\"` — animated transition. Note: smooth restore on back/traverse\n   *   can feel disorienting if the user expects to land at the saved position\n   *   immediately. Recommended for `mode: \"top\"` or anchor scroll only.\n   *\n   * See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior).\n   */\n  behavior?: ScrollBehavior | undefined;\n  /**\n   * sessionStorage key for persisting saved scroll positions. Default:\n   * `\"real-router:scroll\"`. Override only when multiple independent\n   * `RouterProvider` instances share the same document and you need to\n   * isolate their scroll stores (e.g. micro-frontends, embedded widgets,\n   * or testing). For a single app with one provider the default is fine.\n   */\n  storageKey?: string | undefined;\n}\n\ninterface NavigationContext {\n  direction?: \"forward\" | \"back\" | \"unknown\";\n  navigationType?: \"push\" | \"replace\" | \"traverse\" | \"reload\";\n}\n\nexport function createScrollRestoration(\n  router: Router,\n  options?: ScrollRestorationOptions,\n): { destroy: () => void } {\n  if (typeof globalThis.window === \"undefined\") {\n    return NOOP_INSTANCE;\n  }\n\n  const mode = options?.mode ?? \"restore\";\n\n  // mode \"native\" = utility does nothing. Don't flip history.scrollRestoration,\n  // don't subscribe, don't register pagehide — `history.scrollRestoration`\n  // stays at the browser default (\"auto\") so the browser handles scroll\n  // restore natively. (Note: this is the OPPOSITE of `history.scrollRestoration\n  // === \"manual\"` — utility's \"native\" leaves the DOM property at \"auto\" so\n  // the browser is in charge.)\n  if (mode === \"native\") {\n    return NOOP_INSTANCE;\n  }\n\n  const anchorEnabled = options?.anchorScrolling ?? true;\n  const getContainer = options?.scrollContainer;\n  const behavior: ScrollBehavior = options?.behavior ?? \"auto\";\n  const storageKey = options?.storageKey ?? DEFAULT_STORAGE_KEY;\n\n  // Write-through in-memory cache: parse sessionStorage once per provider\n  // mount, then mutate in-memory. Avoids a JSON.parse + JSON.stringify pair\n  // on every subscribeLeave / pagehide event.\n  let store: Record<string, number> | undefined;\n\n  const loadStore = (): Record<string, number> => {\n    if (store !== undefined) {\n      return store;\n    }\n\n    // ⚑ A PROTOTYPE-LESS record, and here that is the cheap fix rather than the\n    // expensive one (#1852). The key is `${route}:${json}`, so both the\n    // skip-same-value READ and the write below consult a chain under a name the\n    // page never chose; with no chain there is nothing to consult. Core pays for\n    // the same guarantee with `putField` because its bags are read on every\n    // render and V8 keeps a prototype-less object in dictionary mode — this one\n    // is a small per-mount cache read a few times per navigation, so the tax is\n    // expected to be negligible and no primitive needs importing.\n    //\n    // ⚠ \"Expected\", not measured, and the word is chosen: nothing benches this\n    // cache. Its sibling in `putField`'s docblock is the reason to be careful —\n    // \"not measurable\" there turned out to be a statement about the instrument,\n    // not the cost. Measure before repeating the word.\n    //\n    // ⚠ `JSON.parse` DEFINES, so a stored `\"__proto__\"` key arrives as ordinary\n    // data either way; what this closes is the ambient half.\n    try {\n      const raw = sessionStorage.getItem(storageKey);\n      const parsed = raw\n        ? (JSON.parse(raw) as Record<string, number>)\n        : undefined;\n\n      store = Object.assign(\n        objectCreate(null) as Record<string, number>,\n        parsed,\n      );\n    } catch {\n      store = objectCreate(null) as Record<string, number>;\n    }\n\n    return store;\n  };\n\n  const putPos = (key: string, pos: number): void => {\n    try {\n      const cached = loadStore();\n\n      // Skip-same-value: when a route is left at the same scroll position it\n      // already holds in the cache (e.g. tab-switching without scrolling),\n      // both the in-memory write and the JSON.stringify + setItem pair are\n      // no-ops. Eliminates redundant serialization on the navigation hot\n      // path for the common \"click tabs without scrolling\" case.\n      if (cached[key] === pos) {\n        return;\n      }\n\n      cached[key] = pos;\n      sessionStorage.setItem(storageKey, JSON.stringify(cached));\n    } catch {\n      // Ignore quota / security errors.\n    }\n  };\n\n  const prevScrollRestoration = history.scrollRestoration;\n\n  try {\n    history.scrollRestoration = \"manual\";\n  } catch {\n    // Ignore — some embedded contexts may reject the assignment.\n  }\n\n  // Resolve the container lazily on every event so containers mounted AFTER\n  // the provider still get correct scroll handling. Falls back to window when\n  // the getter is absent or returns null (pre-mount).\n  const readPos = (): number => {\n    const element = getContainer?.();\n\n    return element ? element.scrollTop : globalThis.scrollY;\n  };\n\n  const writePos = (top: number): void => {\n    const element = getContainer?.();\n\n    if (element) {\n      element.scrollTo({ top, left: 0, behavior });\n    } else {\n      globalThis.scrollTo({ top, left: 0, behavior });\n    }\n  };\n\n  // Restore path (back / traverse / reload). Unlike `writePos`, this tolerates a\n  // scroll container that both MOUNTS and LAYS OUT a few frames AFTER the\n  // navigation settles.\n  //\n  // The capture-side `readPos` always runs against an already-mounted DOM (the\n  // route being left). On restore the target route — and its container — is\n  // still being committed by the view layer. The subscribe callback schedules a\n  // single rAF; for a heavy route (e.g. a long virtual list) the framework's\n  // commit can land AFTER that frame. Two distinct failures follow, each losing\n  // the saved position (Scenario 6 e2e, reproduced under CI's slower runner):\n  //\n  //   1. Container not mounted yet → `getContainer()` is `null`, the scroll\n  //      silently falls back to `window`, which on a container-only route has\n  //      nothing to scroll.\n  //   2. Container mounted but its content not laid out yet → `scrollHeight`\n  //      is still small, so a single `scrollTo({ top })` clamps short of the\n  //      saved position and never re-applies once layout grows.\n  //\n  // With no `scrollContainer` getter the target is always `window`, present\n  // from the first frame — restore in a single shot (unchanged behaviour). When\n  // a getter is configured we cannot tell \"this route legitimately uses window\"\n  // from \"the container is still mounting\", so re-apply the scroll on every\n  // frame for a bounded budget: window as a fallback while the container is\n  // absent (harmless clamp on container routes), the container itself once it\n  // appears. For instant restores we stop early the moment the position sticks;\n  // smooth restores animate asynchronously, so they run the full budget. The\n  // frame budget is the hard backstop against an unreachable target (saved\n  // position taller than the restored content).\n  let restoreToken = 0;\n\n  const restorePos = (top: number): void => {\n    if (!getContainer) {\n      globalThis.scrollTo({ top, left: 0, behavior });\n\n      return;\n    }\n\n    let frames = 0;\n    // ⚑ A per-restore token, the idea `view-transitions.ts` carries as\n    // `scheduledVT` (#781) and this file already carries as `scrollSettled` on\n    // the capture side (#782). Without it the budget is gated by `destroyed`\n    // alone, so a loop whose target is unreachable — a container that clamps\n    // short and keeps retrying — is still running when the next navigation\n    // lands, and writes the PREVIOUS route's offset onto the current page the\n    // moment that container's layout grows (#1924).\n    const token = (restoreToken += 1);\n\n    const attempt = (): void => {\n      if (destroyed || token !== restoreToken) {\n        return;\n      }\n\n      const element = getContainer();\n\n      if (element) {\n        element.scrollTo({ top, left: 0, behavior });\n\n        // Instant restore landed within rounding tolerance → done; no point\n        // re-applying. Smooth restore never matches synchronously, so let it\n        // ride the budget.\n        if (behavior !== \"smooth\" && Math.abs(element.scrollTop - top) <= 1) {\n          return;\n        }\n      } else {\n        globalThis.scrollTo({ top, left: 0, behavior });\n      }\n\n      if (frames >= RESTORE_RETRY_FRAMES) {\n        return;\n      }\n\n      frames += 1;\n      requestAnimationFrame(attempt);\n    };\n\n    attempt();\n  };\n\n  const scrollToHashOrTop = (route: State): void => {\n    // URL plugin path (#532): `state.context.url.hash` is the source of truth\n    // when one of the URL plugins (browser-plugin / navigation-plugin) is\n    // installed. The value is already DECODED — feeding it through\n    // `decodeURIComponent` again would throw on a bare `%`.\n    const ctxHash = (route.context as { url?: { hash?: string } } | undefined)\n      ?.url?.hash;\n\n    if (ctxHash !== undefined) {\n      if (anchorEnabled && ctxHash.length > 0) {\n        // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n        const element = document.getElementById(ctxHash);\n\n        if (element) {\n          element.scrollIntoView({ behavior });\n\n          return;\n        }\n      }\n\n      writePos(0);\n\n      return;\n    }\n\n    // Fallback path: no URL plugin, read the DOM. `location.hash` is\n    // percent-encoded; ids in the DOM are the raw string, so decode for the\n    // match. Fall back to the raw slice if the hash contains a malformed\n    // escape sequence (decodeURIComponent throws on those).\n    const hash = globalThis.location.hash;\n\n    if (anchorEnabled && hash.length > 1) {\n      let id: string;\n\n      try {\n        id = decodeURIComponent(hash.slice(1));\n      } catch {\n        id = hash.slice(1);\n      }\n\n      // eslint-disable-next-line unicorn/prefer-query-selector -- ids may contain CSS-unsafe chars\n      const element = document.getElementById(id);\n\n      if (element) {\n        element.scrollIntoView({ behavior });\n\n        return;\n      }\n    }\n\n    writePos(0);\n  };\n\n  let destroyed = false;\n  // Capture/effect seam guard (#782). previousRoute's position is captured\n  // synchronously in `subscribe`, but the snap/restore effect runs a frame\n  // later in rAF. Across that window the viewport still shows the route BEFORE\n  // previousRoute, so a second navigation landing in the same frame would\n  // capture that foreign position under previousRoute's key. `scrollSettled` is\n  // false across the window — capture is skipped (previousRoute's own stored\n  // value survives the transit). A real user scroll in this <16ms window is\n  // physically impossible.\n  let scrollSettled = true;\n\n  const unsubscribe = router.subscribe(({ route, previousRoute }) => {\n    const nav = (route.context as { navigation?: NavigationContext })\n      .navigation;\n\n    // Browsers dispatch reload as the initial navigation after refresh, so\n    // previousRoute is undefined and capture is naturally skipped. The\n    // pre-refresh position was already persisted via pagehide. Capture is also\n    // skipped while the scroll is unsettled — a second navigation in the same\n    // frame, before the prior nav's rAF snap (see `scrollSettled`, #782).\n    if (previousRoute && scrollSettled) {\n      putPos(keyOf(previousRoute), readPos());\n    }\n\n    // This navigation's scroll effect is now pending: the viewport position no\n    // longer belongs to `route` until the rAF below runs and settles it.\n    scrollSettled = false;\n\n    requestAnimationFrame(() => {\n      if (destroyed) {\n        return;\n      }\n\n      // Effect running — the position now belongs to `route`, so the next\n      // capture is honest again.\n      scrollSettled = true;\n\n      if (mode === \"top\") {\n        scrollToHashOrTop(route);\n\n        return;\n      }\n\n      // Restore branches (reload, back/traverse) MUST be evaluated before the\n      // replace-skip below. Since #657 lifted `replace` into TransitionMeta, a\n      // history TRAVERSAL (back/forward) under navigation-plugin carries\n      // `transition.replace === true` — a traversal reuses an existing history\n      // entry, which is replace-shaped at the history level. If the replace-skip\n      // ran first it would swallow every back/forward navigation and restore\n      // would never fire (the Scenario 6 e2e regression). Genuine in-place\n      // replaces (`router.navigate({ replace: true })`, navigateToNotFound) are\n      // not traversals and fall through to the skip below.\n      //\n      // Both arms of each check are required: `transition.reload` only fires for\n      // programmatic `router.navigate({reload:true})`. F5 under navigation-plugin\n      // primes `nav.navigationType === \"reload\"` via #531 getActivationType but\n      // leaves opts.reload undefined, so dropping the plugin arm would regress F5\n      // scroll-restore. Browser-plugin's F5 is not covered (no priming, out of\n      // scope).\n      // `?.` on a required field: core commits a foreign State's ABSENT\n      // `transition` rather than fabricating one (#1792 / #1976), and this runs\n      // on whatever `subscribe` hands over. Absent falls through to the plugin\n      // arm, which is the pre-#1976 answer for a state carrying no meta.\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the required field is genuinely absent on a foreign committed State\n      if (route.transition?.reload || nav?.navigationType === \"reload\") {\n        restorePos(loadStore()[keyOf(route)] ?? 0);\n\n        return;\n      }\n\n      if (nav?.direction === \"back\" || nav?.navigationType === \"traverse\") {\n        restorePos(loadStore()[keyOf(route)] ?? 0);\n\n        return;\n      }\n\n      // Genuine in-place replace (not a traversal) — leave scroll untouched.\n      // `?.` for the same reason as the reload arm above.\n      // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the required field is genuinely absent on a foreign committed State\n      if (route.transition?.replace || nav?.navigationType === \"replace\") {\n        return;\n      }\n\n      scrollToHashOrTop(route);\n    });\n  });\n\n  const onPageHide = (): void => {\n    const current = router.getState();\n\n    if (current) {\n      putPos(keyOf(current), readPos());\n    }\n  };\n\n  globalThis.addEventListener(\"pagehide\", onPageHide);\n\n  return {\n    destroy: () => {\n      // No `if (destroyed) return` guard: every teardown below is idempotent —\n      // `unsubscribe()` is a `set.delete` in the core EventEmitter, DOM\n      // `removeEventListener` is spec-idempotent, and the history assignment is\n      // a plain re-set. There is no ref-count to protect (unlike\n      // route-announcer's shared announcer element), so a double `destroy()` is\n      // harmless. `destroyed = true` still gates any pending restore rAF / retry.\n      destroyed = true;\n      unsubscribe();\n      globalThis.removeEventListener(\"pagehide\", onPageHide);\n\n      try {\n        history.scrollRestoration = prevScrollRestoration;\n      } catch {\n        // Ignore.\n      }\n    },\n  };\n}\n\n/**\n * Internal cache-key builder for scroll-position storage.\n *\n * **Exported for testing only — not part of the public API** (intentionally\n * excluded from `index.ts` barrel). Adapter property tests import it rather\n * than replicating it (§8b H20 / audit-2026-05-16 #S3): a replica drifts\n * silently the moment the key changes. A change to the key format loses saved\n * positions across an upgrade, so the test set is the contract.\n *\n * ## Not memoized\n *\n * The key is a string property read, which is cheaper than the `WeakMap`\n * lookup any cache would need — so there is nothing here to cache.\n */\nexport function keyOf(state: State): string {\n  // The key is the LOCATION, and `state.path` is the form core prints it in\n  // (#1923). Deriving it from the bags instead makes this a SECOND place that\n  // has to know how a value prints — and the two domains disagree there: the\n  // URL direction parses `?page=2` into the number `2`, an intent keeps `\"2\"`,\n  // and `packages/core/src/helpers.ts` says comparison is the single place that\n  // knows they describe one location. Reading the printed form asks core rather\n  // than re-deriving it, so a route's query, its `?id` carve-out twin and the\n  // order its params were written in are all already settled here.\n  return state.path;\n}\n","import { getTransitionSource } from \"@real-router/sources\";\n\nimport type { NavigationOptions, Router } from \"@real-router/core\";\n\n/**\n * Router-coordinated scroll spy (#575).\n *\n * On `IntersectionObserver` notifications the utility picks the topmost\n * visible anchor inside the configured scroll container and emits a forced\n * same-route transition with `{ hash, replace: true, force: true, hashChange:\n * true }` through `router.navigate(...)`. The URL plugin\n * (`@real-router/browser-plugin` or `@real-router/navigation-plugin`) updates\n * `state.context.url.hash` so sibling hash-aware `<Link hash>` re-highlights\n * via the standard `createActiveRouteSource` pipeline.\n *\n * **Anti-flicker gates** (RFC §5.2):\n * 1. `getTransitionSource(router).getSnapshot().isTransitioning` — skip emits\n *    while a transition is in-flight (re-entrant lock).\n * 2. `coolingDown` — set on a user-driven hash transition (e.g. `<Link hash>`\n *    click + smooth `scrollIntoView`). Cleared on `scrollend` or after a\n *    500ms safety timeout. Spy's own emits are excluded via the synchronous\n *    `selfEmitting` flag — required so the spy doesn't rate-limit itself.\n *\n * **Self-healing** (RFC §7.3): if the initial URL contains a hash without a\n * matching `id` (e.g. `/page#nonexistent`), the first IO event emitted right\n * after observe()-ing picks the topmost real anchor and corrects the URL.\n *\n * **Hash-only transition pipeline cost** (RFC §5.3): for same-route same-\n * params hash-only navigations, `getTransitionPath` returns empty\n * `toDeactivate` / `toActivate` arrays, so `runGuards` is a no-op. The only\n * work is the URL plugin's `onTransitionSuccess` write and the\n * `getTransitionSource` flip — cheap.\n *\n * **Architecture**: decomposed into 4 private subsystem closure factories\n * (`createUrlPluginDetector`, `createCooldown`, `createDebouncer`,\n * `createObserverPair`). The main `createScrollSpy` wires them together\n * around the shared `silenced` / `destroyed` / `selfEmitting` flags and the\n * `flush()` emit logic. Each subsystem owns its state + cleanup; `destroy()`\n * delegates to each. See section banners below.\n *\n * @returns A `ScrollSpy` handle whose `destroy()` is idempotent.\n */\nexport interface ScrollSpyOptions {\n  /**\n   * CSS selector for anchor candidates. Empty string `\"\"` or `undefined`\n   * disables the spy (returns a NOOP handle). Common values:\n   * `\"[id]\"`, `\"[id]:is(h1,h2,h3)\"`, `\"section[id]\"`.\n   */\n  selector: string;\n\n  /**\n   * `IntersectionObserver` `rootMargin`. Default\n   * `\"-20% 0px -60% 0px\"` — an anchor is considered \"active\" once it crosses\n   * into the top 20 % of the viewport (or scroll container).\n   */\n  rootMargin?: string | undefined;\n\n  /**\n   * Lazy getter for the scrollable container. Consulted at creation and\n   * re-consulted on every reconcile (DOM mutation), so a container that\n   * MOUNTS or CHANGES after the spy is created is honoured: the\n   * `IntersectionObserver` root and `MutationObserver` target — both immutable\n   * once constructed — are rebuilt to match (#780). `null` (or a missing\n   * getter) falls back to the window viewport (`root: null` on the\n   * `IntersectionObserver`).\n   */\n  scrollContainer?: (() => HTMLElement | null) | undefined;\n}\n\nexport interface ScrollSpy {\n  /** Tear down observer + listeners. Idempotent. */\n  destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ScrollSpy = Object.freeze({\n  destroy: () => {\n    /* no-op */\n  },\n});\n\n// Hardcoded internals (RFC §5.1 — promote only with evidence).\nconst RAF_DEBOUNCE_MS = 150;\nconst MUTATION_DEBOUNCE_MS = 250;\nconst COOLDOWN_TIMEOUT_MS = 500;\nconst DEFAULT_ROOT_MARGIN = \"-20% 0px -60% 0px\";\n\n// Local extension type — browser-plugin / navigation-plugin augment\n// `NavigationOptions` with `hash` and `hashChange`, but `shared/dom-utils`\n// is plugin-agnostic and cannot rely on the augmentation. Mirrors the\n// `HashAwareNavigationOptions` pattern in `link-utils.ts`.\ntype HashAwareNavigationOptions = NavigationOptions & {\n  hash?: string;\n  hashChange?: boolean;\n};\n\n// The `url` namespace contract is owned by browser-env: both URL plugins\n// (browser-plugin, navigation-plugin) write `{ hash: string; hashChanged }` on\n// every transition. This is a local mirror (keeps dom-utils independent of\n// browser-env) and must match that canonical shape — `hash` is always present,\n// never a partial slice.\ninterface UrlContextSlice {\n  hash: string;\n  hashChanged: boolean;\n}\n\nconst getUrlContext = (state: {\n  context?: unknown;\n}): UrlContextSlice | undefined =>\n  (state.context as { url?: UrlContextSlice } | undefined)?.url;\n\n// =============================================================================\n// Picker — pure, no state. RFC §5.2 selection rule.\n// =============================================================================\n\n// Pick the anchor closest to the active zone top in viewport coordinates.\n// `entry.rootBounds.top` already reflects `rootMargin` (per W3C IO spec\n// §3.3) — for `rootMargin: \"-20% 0px -60% 0px\"` it returns 20% of root\n// height, for `\"-50% 0px -50% 0px\"` it returns the center, etc. Distance\n// = boundingClientRect.top − zoneTop in viewport pixels: positive = anchor\n// below zone top (just entered), negative = anchor above zone top (body\n// crossing zone from above). We prefer smallest non-negative; fall back to\n// least-negative when no entry has crossed yet.\n// Falls back to zoneTop = 0 when rootBounds is null (cross-origin roots,\n// unit tests). Single pass — handles `Iterable` so flushes can pass\n// `Map.values()` directly without realising the array.\nconst pickTopmost = (\n  entries: Iterable<IntersectionObserverEntry>,\n): IntersectionObserverEntry | null => {\n  let bestPositive: IntersectionObserverEntry | null = null;\n  let bestPositiveDist = Number.POSITIVE_INFINITY;\n  let bestNegative: IntersectionObserverEntry | null = null;\n  let bestNegativeDist = Number.NEGATIVE_INFINITY;\n\n  for (const entry of entries) {\n    if (!entry.isIntersecting) {\n      continue;\n    }\n\n    const zoneTop = entry.rootBounds?.top ?? 0;\n    const distance = entry.boundingClientRect.top - zoneTop;\n\n    if (distance >= 0) {\n      if (distance < bestPositiveDist) {\n        bestPositive = entry;\n        bestPositiveDist = distance;\n      }\n    } else if (distance > bestNegativeDist) {\n      bestNegative = entry;\n      bestNegativeDist = distance;\n    }\n  }\n\n  return bestPositive ?? bestNegative;\n};\n\n// =============================================================================\n// Subsystem: URL plugin detector (RFC §5.5)\n// Calls `onMissing` if `state.context` is published but `url` key is missing\n// (i.e. no URL plugin installed). Either synchronous on start, or deferred\n// via a one-shot `router.subscribe` if the router has not started yet.\n// `silenced` flag itself lives in main scope — detector signals via callback\n// (per Oracle Q1 — `silenced` has multiple unrelated triggers; main scope\n// owns the kill switch).\n// =============================================================================\n\ninterface UrlPluginDetector {\n  destroy: () => void;\n}\n\nconst createUrlPluginDetector = (\n  router: Router,\n  onMissing: () => void,\n): UrlPluginDetector => {\n  let detectionUnsub: (() => void) | null = null;\n\n  const verify = (state: { context?: unknown }): void => {\n    const context = state.context as\n      (Record<string, unknown> & { url?: unknown }) | undefined;\n\n    if (context && context.url === undefined) {\n      console.warn(\n        \"[real-router] scroll-spy: state.context.url is not claimed. \" +\n          \"Spy requires browser-plugin or navigation-plugin. Disabling.\",\n      );\n      onMissing();\n    }\n  };\n\n  const peekState = router.getState();\n\n  if (peekState) {\n    verify(peekState);\n  } else {\n    // Re-entry guard: `router.subscribe` MAY invoke the callback synchronously\n    // from inside `.subscribe(...)` before the function returns. In that case\n    // `detectionUnsub` is still `null` when the callback fires. Without this\n    // boolean, a hypothetical multi-fire would double-warn.\n    let detectionConsumed = false;\n\n    detectionUnsub = router.subscribe(({ route }) => {\n      /* v8 ignore next 3 -- @preserve: the multi-fire is hypothetical (see above) — the real router never invokes a subscriber synchronously twice before unsub; defensive guard, not testable without a contract-violating fake */\n      if (detectionConsumed) {\n        return;\n      }\n\n      detectionConsumed = true;\n      verify(route);\n\n      detectionUnsub?.();\n      detectionUnsub = null;\n    });\n  }\n\n  return {\n    destroy(): void {\n      detectionUnsub?.();\n      detectionUnsub = null;\n    },\n  };\n};\n\n// =============================================================================\n// Subsystem: Cooldown gate (RFC §5.2 — anti-flicker for smooth scrollIntoView)\n// Set on user-driven `<Link hash>` click → smooth scroll. Cleared on\n// `scrollend` (Baseline 2026) or 500ms safety timeout (older Safari).\n// =============================================================================\n\ninterface Cooldown {\n  readonly active: boolean;\n  start: () => void;\n  destroy: () => void;\n}\n\nconst createCooldown = (getContainer: () => HTMLElement | null): Cooldown => {\n  let active = false;\n  let timeout: ReturnType<typeof setTimeout> | null = null;\n  let listenerContainer: HTMLElement | null = null;\n  let listener: (() => void) | null = null;\n\n  const clear = (): void => {\n    if (timeout !== null) {\n      clearTimeout(timeout);\n      timeout = null;\n    }\n\n    if (listener) {\n      const target: EventTarget = listenerContainer ?? globalThis;\n\n      target.removeEventListener(\"scrollend\", listener);\n    }\n\n    listener = null;\n    listenerContainer = null;\n    active = false;\n  };\n\n  return {\n    get active(): boolean {\n      return active;\n    },\n    start(): void {\n      // Reset rather than stack timers if cooldown is already active.\n      clear();\n\n      active = true;\n\n      const lift = (): void => {\n        clear();\n      };\n\n      listener = lift;\n      listenerContainer = getContainer();\n\n      const target: EventTarget = listenerContainer ?? globalThis;\n\n      target.addEventListener(\"scrollend\", lift, { once: true });\n\n      timeout = setTimeout(lift, COOLDOWN_TIMEOUT_MS);\n    },\n    destroy(): void {\n      clear();\n    },\n  };\n};\n\n// =============================================================================\n// Subsystem: rAF + trailing debounce (RFC §5.1)\n// Coalesces a burst of IO events into ≤ 1 callback per debounce window.\n// rAF reduces N setTimeout creations to 1 per animation frame; the trailing\n// 150ms setTimeout waits for the IO stream to quiesce.\n// =============================================================================\n\ninterface Debouncer {\n  schedule: () => void;\n  destroy: () => void;\n}\n\nconst createDebouncer = (\n  callback: () => void,\n  trailingMs: number,\n): Debouncer => {\n  let raf: number | null = null;\n  let timeout: ReturnType<typeof setTimeout> | null = null;\n\n  return {\n    schedule(): void {\n      if (raf !== null) {\n        return;\n      }\n\n      raf = requestAnimationFrame(() => {\n        raf = null;\n\n        if (timeout !== null) {\n          clearTimeout(timeout);\n        }\n\n        timeout = setTimeout(() => {\n          timeout = null;\n          callback();\n        }, trailingMs);\n      });\n    },\n    destroy(): void {\n      if (raf !== null) {\n        cancelAnimationFrame(raf);\n        raf = null;\n      }\n\n      if (timeout !== null) {\n        clearTimeout(timeout);\n        timeout = null;\n      }\n    },\n  };\n};\n\n// =============================================================================\n// Subsystem: Observer pair (IntersectionObserver + MutationObserver)\n// IO + MO genuinely form one subsystem — both write/read `observed` set and\n// `pending` map, and reconcile flow couples them. Per Oracle Q10, splitting\n// would force cross-subsystem references that re-introduce the wiring\n// problem we're trying to solve.\n//\n// Exposes `pending` directly (per Oracle Q4: hiding behind `consume()` adds\n// boilerplate without isolating the shared mutable state — observers write\n// from IO callbacks while main scope reads in `flush()`).\n// =============================================================================\n\ninterface ObserverPair {\n  readonly pending: Map<Element, IntersectionObserverEntry>;\n  /** True when a resolved container has since detached from the DOM (#1216). */\n  isContainerDetached: () => boolean;\n  /** Re-resolve the container + re-observe matches (rebuilds the pair on change). */\n  reconcile: () => void;\n  destroy: () => void;\n}\n\nconst createObserverPair = (\n  selector: string,\n  rootMargin: string,\n  getContainer: () => HTMLElement | null,\n  onIntersection: () => void,\n  onInvalidSelector: () => void,\n  isStopped: () => boolean,\n): ObserverPair => {\n  const observed = new Set<Element>();\n  // Latest IO entry per target — accumulated across batches. IO delivers\n  // entries only for targets whose intersection state CHANGED (W3C IO\n  // §3.2.1), so a fast scroll that lands two callbacks inside the same\n  // debounce window must merge by target, not overwrite. Entries are\n  // dropped from the map when their target leaves the DOM (see `reconcile`)\n  // and on `destroy()`.\n  const pending = new Map<Element, IntersectionObserverEntry>();\n\n  let duplicateIdWarned = false;\n  let mutationTimer: ReturnType<typeof setTimeout> | null = null;\n\n  const handleIntersection: IntersectionObserverCallback = (entries) => {\n    // Defensive: IO callback may fire AFTER `destroy()` if a queued event\n    // was already scheduled by the browser before `disconnect()`. Cheap\n    // belt-and-suspenders.\n    if (isStopped()) {\n      return;\n    }\n\n    for (const entry of entries) {\n      pending.set(entry.target, entry);\n    }\n\n    onIntersection();\n  };\n\n  // Build (and rebuild) the IntersectionObserver for a given root. A root is\n  // immutable once its IO is constructed, so `reconcile` recreates the IO via\n  // this same factory when the resolved container changes (#780) — one\n  // definition, two call sites.\n  const makeIo = (container: HTMLElement | null): IntersectionObserver =>\n    new IntersectionObserver(handleIntersection, {\n      root: container,\n      rootMargin,\n      threshold: 0,\n    });\n\n  // Container the IntersectionObserver root + MutationObserver target are\n  // built with. Both are immutable once the observer is constructed (W3C), so\n  // a `scrollContainer` that resolves to a different element after creation —\n  // most importantly one that MOUNTS after the spy starts (Angular wires the\n  // spy at bootstrap, before any component renders; a docs route's container\n  // mounts on navigation) — is only honoured by rebuilding the pair in\n  // `reconcile`. Tracked here so `reconcile` can compare on every run (#780).\n  let observerContainer = getContainer();\n\n  let io = makeIo(observerContainer);\n\n  const observeMatches = (): void => {\n    const scope = getContainer() ?? document;\n    let candidates: NodeListOf<Element>;\n\n    try {\n      candidates = scope.querySelectorAll(selector);\n    } catch {\n      onInvalidSelector();\n\n      return;\n    }\n\n    const seenIds = new Set<string>();\n\n    for (const element of candidates) {\n      // Detect duplicate ids once (RFC §7.7). The DOM permits duplicate ids\n      // even though it is a markup bug; the spy keeps working but picks the\n      // first one deterministically via the topmost-visible rule.\n      const id = (element as HTMLElement).id;\n\n      if (id && !duplicateIdWarned) {\n        if (seenIds.has(id)) {\n          duplicateIdWarned = true;\n\n          console.warn(\n            `[real-router] scroll-spy: duplicate id \"${id}\" observed. ` +\n              \"Selection picks the topmost visible match deterministically.\",\n          );\n        }\n\n        seenIds.add(id);\n      }\n\n      if (observed.has(element)) {\n        continue;\n      }\n\n      io.observe(element);\n      observed.add(element);\n    }\n  };\n\n  // MutationObserver init — reused when the observer is re-pointed at a new\n  // container in `reconcile`. `childList: true, subtree: true` catches\n  // structural changes; `attributes: true, attributeFilter: [\"id\"]` catches\n  // anchor id renames (typical for client-rendered docs). The MO targets the\n  // scroll container (or document.body for the window viewport).\n  const MUTATION_OBSERVE_INIT: MutationObserverInit = {\n    childList: true,\n    subtree: true,\n    attributes: true,\n    attributeFilter: [\"id\"],\n  };\n\n  // Null-then-assigned — the same forward-reference idiom as `flush` in the\n  // main scope. `reconcile` re-points it on a container change, so it must be\n  // in scope above its assignment; it is non-null by the time any async\n  // mutation callback (or `reconcile`) runs.\n  let mo: MutationObserver | null = null;\n\n  const reconcile = (): void => {\n    // Drop observed elements that left the DOM. Avoids observer holding\n    // strong refs to detached nodes. Also drop their accumulated entry so\n    // stale \"was intersecting\" state for a removed node cannot be picked\n    // by `pickTopmost` after the node is gone.\n    for (const element of observed) {\n      if (element.isConnected) {\n        continue;\n      }\n\n      io.unobserve(element);\n      observed.delete(element);\n      pending.delete(element);\n    }\n\n    // Honour a container that mounted (or changed) after construction (#780).\n    // The IntersectionObserver root and MutationObserver target cannot be\n    // mutated in place, so rebuild the pair under the new container. Clearing\n    // `observed` + `pending` makes the rebuild equivalent to constructing the\n    // spy with this container from the start: `observeMatches` below\n    // re-populates the tracked set from the new container's scope, and the\n    // stale merged snapshot (computed against the old root's geometry) is\n    // dropped — one empty debounce window, acceptable for a rare event.\n    const nextContainer = getContainer();\n\n    if (nextContainer !== observerContainer) {\n      observerContainer = nextContainer;\n\n      io.disconnect();\n      io = makeIo(nextContainer);\n      observed.clear();\n      pending.clear();\n\n      mo?.disconnect();\n      mo?.observe(nextContainer ?? document.body, MUTATION_OBSERVE_INIT);\n    }\n\n    observeMatches();\n  };\n\n  observeMatches();\n\n  mo = new MutationObserver(() => {\n    if (mutationTimer !== null) {\n      clearTimeout(mutationTimer);\n    }\n\n    mutationTimer = setTimeout(() => {\n      mutationTimer = null;\n      reconcile();\n    }, MUTATION_DEBOUNCE_MS);\n  });\n\n  mo.observe(observerContainer ?? document.body, MUTATION_OBSERVE_INIT);\n\n  return {\n    pending,\n    // #1216: the MutationObserver is pointed at the container's OWN subtree, so\n    // the container's removal (a mutation of its PARENT) is invisible — reconcile\n    // never fires on it and a remounted container is never re-observed. Expose a\n    // detach check + reconcile so the router.subscribe callback can re-resolve on\n    // navigation (exactly when route-tied containers mount/die). When\n    // `observerContainer` is null the MO already watches `document.body`, which\n    // sees container mounts directly — so only a resolved-then-detached container\n    // needs this nav-time nudge.\n    isContainerDetached: (): boolean =>\n      observerContainer !== null && !observerContainer.isConnected,\n    reconcile,\n    destroy(): void {\n      io.disconnect();\n      mo.disconnect();\n\n      if (mutationTimer !== null) {\n        clearTimeout(mutationTimer);\n        mutationTimer = null;\n      }\n\n      observed.clear();\n      pending.clear();\n    },\n  };\n};\n\n// =============================================================================\n// Main: compositional wiring\n// =============================================================================\n\nexport function createScrollSpy(\n  router: Router,\n  options: ScrollSpyOptions,\n): ScrollSpy {\n  // SSR guard (RFC §7.5) — return early without warnings.\n  if (typeof document === \"undefined\") {\n    return NOOP_INSTANCE;\n  }\n\n  // Feature-detect IntersectionObserver — no polyfill ships (RFC §4).\n  if (typeof IntersectionObserver === \"undefined\") {\n    return NOOP_INSTANCE;\n  }\n\n  const { selector } = options;\n\n  // Empty selector → disabled. Documented opt-out for conditional enabling\n  // (RFC §5.4 `scrollSpy={{ selector: enable ? \"[id]\" : \"\" }}`).\n  if (!selector) {\n    return NOOP_INSTANCE;\n  }\n\n  const rootMargin = options.rootMargin ?? DEFAULT_ROOT_MARGIN;\n  const getContainer = options.scrollContainer;\n  const resolveContainer = (): HTMLElement | null => getContainer?.() ?? null;\n\n  // Shared lifecycle flags (Oracle Q1 — `silenced` has multiple unrelated\n  // triggers; Oracle Q3 — `selfEmitting` synchronously bracketed around\n  // `router.navigate()` cannot cleanly extract). Kept in main scope.\n  let destroyed = false;\n  let silenced = false;\n  let selfEmitting = false;\n\n  const isStopped = (): boolean => silenced || destroyed;\n\n  // Symmetric late-binding (Oracle Q2): declare `flush` as nullable, wire\n  // debouncer + observers, then assign the real implementation. Reads as\n  // intentional wiring rather than accidental closure capture ordering.\n  // The `flush?.()` call below safely no-ops if a callback somehow fires\n  // before assignment (impossible in practice — IO/debounce are async).\n  let flush: (() => void) | null = null;\n\n  const transitionSource = getTransitionSource(router);\n\n  const detector = createUrlPluginDetector(router, () => {\n    silenced = true;\n  });\n\n  const cooldown = createCooldown(resolveContainer);\n\n  const debouncer = createDebouncer(() => {\n    flush?.();\n  }, RAF_DEBOUNCE_MS);\n\n  const observers = createObserverPair(\n    selector,\n    rootMargin,\n    resolveContainer,\n    () => {\n      debouncer.schedule();\n    },\n    () => {\n      if (silenced) {\n        return;\n      }\n\n      silenced = true;\n\n      console.warn(\n        `[real-router] scroll-spy: invalid selector \"${selector}\". Disabling.`,\n      );\n    },\n    isStopped,\n  );\n\n  flush = (): void => {\n    if (destroyed || silenced) {\n      observers.pending.clear();\n\n      return;\n    }\n\n    // Gate-skipped flushes keep `pendingEntries` populated — the merged\n    // state is still the best-known snapshot, and the next non-gated flush\n    // consumes it. Clearing under a gate would re-introduce the overwrite\n    // bug for any anchor whose intersection state did not change during\n    // the gate window.\n    if (transitionSource.getSnapshot().isTransitioning) {\n      return;\n    }\n\n    if (cooldown.active) {\n      return;\n    }\n\n    // No `if (pending.size === 0) return` fast-path: `pending` is never empty\n    // here via any real path (a real IntersectionObserver always delivers ≥1\n    // entry, so `handleIntersection` populates `pending` before scheduling; the\n    // mutation reconcile that could drop entries runs at MUTATION_DEBOUNCE_MS\n    // 250 > RAF_DEBOUNCE_MS 150, i.e. always AFTER the flush). And an empty map\n    // is already handled identically below — `pickTopmost(∅)` is `null` → the\n    // `if (!picked) return` guard — so the fast-path was both unreachable and\n    // redundant.\n\n    // Successful flush consumes the merged snapshot. We clear so that the\n    // next debounce window starts fresh; an anchor that is still\n    // intersecting will only stay observable if IO emits another event for\n    // it (which it does whenever the anchor's intersection state actually\n    // changes). Skipping the clear here would leak state from one user-\n    // perceived \"scroll stop\" into the next.\n    const picked = pickTopmost(observers.pending.values());\n\n    observers.pending.clear();\n\n    if (!picked) {\n      // No anchor visible / above zone — preserve last hash (RFC §10 #5).\n      return;\n    }\n\n    const newHash = (picked.target as HTMLElement).id;\n\n    if (!newHash) {\n      return;\n    }\n\n    const state = router.getState();\n\n    if (!state) {\n      return;\n    }\n\n    // `getUrlContext` is guaranteed present here (the URL-plugin detector\n    // silences the spy otherwise), so `?.` only satisfies the `| undefined`\n    // slice type. `newHash` is a non-empty id (guarded above), so it can never\n    // equal an absent hash — no `?? \"\"` normalization needed for the compare.\n    const currentHash = getUrlContext(state)?.hash;\n\n    if (newHash === currentHash) {\n      return;\n    }\n\n    // Emit the same-route same-params hash-only transition. URL plugin\n    // writes `state.context.url.hash = newHash` + `hashChanged = true` in\n    // its `onTransitionSuccess` claim.\n    const opts: HashAwareNavigationOptions = {\n      hash: newHash,\n      replace: true,\n      force: true,\n      hashChange: true,\n    };\n\n    // Self-emit guard (RFC §5.2): set synchronously around our own\n    // `router.navigate()` so the `router.subscribe` callback skips the\n    // cooldown setup for spy-emitted transitions — otherwise spy would\n    // rate-limit itself to ≤ 2 emits/s, contradicting the ≤ 10/s benchmark\n    // target. Test coupling (Q8): preserve exact `.catch(noop).finally(reset)`\n    // chain — migrating to `try/finally` over `await router.navigate(...)`\n    // changes microtask schedule and breaks \"spy continues after rejection\".\n    selfEmitting = true;\n    router\n      // Both channels of the CURRENT state, not just the path bag (RFC-4 M2 /\n      // #1548): this is a same-route re-navigation to move the hash, so\n      // whatever query the user is already looking at has to survive it.\n      // Passing `undefined` in slot 3 dropped it — scrolling a page at\n      // `/docs?tab=api` silently rewrote the URL to `/docs`. Options stay at\n      // slot 4.\n      .navigate(state.name, state.params, state.search, opts)\n      .catch(() => {\n        // Fire-and-forget — suppress expected rejections (concurrent\n        // navigate, router stopped, etc.) consistent with `<Link>` adapter\n        // patterns.\n      })\n      .finally(() => {\n        selfEmitting = false;\n      });\n  };\n\n  // Cooldown setup on user-driven hash transitions. Spy's own emits are\n  // distinguished via the synchronous `selfEmitting` flag (see `flush`).\n  const unsubscribeRouter = router.subscribe(({ route }) => {\n    if (selfEmitting) {\n      return;\n    }\n\n    // #1216: a route-tied scroll container may have unmounted since the last\n    // navigation. The container-scoped MutationObserver can't observe its own\n    // removal (a mutation of its parent), so re-resolve + re-observe here —\n    // navigation is exactly when such containers mount / die.\n    if (observers.isContainerDetached()) {\n      observers.reconcile();\n    }\n\n    if (getUrlContext(route)?.hashChanged) {\n      cooldown.start();\n    }\n  });\n\n  return {\n    destroy(): void {\n      // No `if (destroyed) return` idempotency guard: every subsystem teardown\n      // below is itself idempotent (null-guarded timers, `set.delete` on the\n      // router unsubscribe, spec-idempotent `IntersectionObserver.disconnect`),\n      // and `destroy()` is not a hot path — so a redundant guard would only add\n      // an unreachable branch. `destroyed = true` is still set to gate any\n      // late-arriving IO/router callback via `isStopped()`.\n      destroyed = true;\n\n      // Unsubscribe FIRST to prevent late-arriving router transition\n      // callback from calling `cooldown.start()` on a half-destroyed\n      // instance. Without this ordering, a transition with `hashChanged:\n      // true` firing between subsystem teardown and `unsubscribeRouter()`\n      // would re-install a 500ms timer that survives `destroy()`. Verified\n      // via Oracle review (Q5/Q7).\n      unsubscribeRouter();\n\n      observers.destroy();\n      debouncer.destroy();\n      cooldown.destroy();\n      detector.destroy();\n    },\n  };\n}\n","import type { Router } from \"@real-router/core\";\n\nexport interface ViewTransitions {\n  destroy: () => void;\n}\n\nconst NOOP_INSTANCE: ViewTransitions = Object.freeze({\n  destroy: () => {\n    /* no-op */\n  },\n});\n\nexport function createViewTransitions(router: Router): ViewTransitions {\n  if (\n    typeof document === \"undefined\" ||\n    typeof document.startViewTransition !== \"function\"\n  ) {\n    return NOOP_INSTANCE;\n  }\n\n  let closeVT: (() => void) | null = null;\n  let currentVT: { skipTransition?: () => void } | null = null;\n  // Tracks whether TRANSITION_SUCCESS fired for the current leave. Used to\n  // distinguish \"benign cleanup abort\" (router's async path aborts its own\n  // controller in a finally block after successful navigation) from \"real\n  // cancellation\" (concurrent navigate, guard rejection, dispose).\n  let successFired = false;\n\n  const resolveAndClear = (): void => {\n    closeVT?.();\n    closeVT = null;\n  };\n\n  const offLeave = router.subscribeLeave(({ signal }) => {\n    // Reentrant abort: signal already aborted when we're called. Open no VT\n    // — router will fall through to TRANSITION_CANCELLED via isCurrentNav()\n    // after leave resolves. addEventListener(\"abort\", ...) does not re-fire\n    // for past events, so skipping startViewTransition is the safe path.\n    if (signal.aborted) {\n      return;\n    }\n\n    successFired = false;\n    resolveAndClear();\n\n    // Return a Promise so the router awaits until the browser invokes\n    // updateCallback. This ensures old DOM snapshot is captured BEFORE the\n    // router commits the new state — giving correct exit→state→entry\n    // ordering (vs fire-and-forget, where URL changes before VT captures).\n    return new Promise<void>((resolveLeave) => {\n      // Capture the resolver synchronously BEFORE startViewTransition() is\n      // called. The browser invokes updateCallback in a later task, but\n      // router.subscribe (TRANSITION_SUCCESS) can fire before that. If we\n      // captured `resolve` inside the callback, subscribe would see closeVT\n      // still null and skip resolving — the deferred would hang for 4s\n      // until the VT API aborts with TimeoutError.\n      // eslint-disable-next-line unicorn/prefer-promise-with-resolvers -- frozen shared primitive; resolver captured synchronously before startViewTransition() by design (see comment above)\n      const deferred = new Promise<void>((resolve) => {\n        closeVT = resolve;\n      });\n\n      signal.addEventListener(\n        \"abort\",\n        () => {\n          if (successFired) {\n            // Router's async path (#finishAsyncNavigation) aborts its own\n            // controller in a finally block AFTER completeTransition (and\n            // thus AFTER subscribe fired). This is cleanup, not\n            // cancellation — VT is progressing normally, do nothing.\n            return;\n          }\n\n          // Real cancellation (concurrent navigate, dispose). Resolve the\n          // deferred so updateCallback can complete, skip the VT so no\n          // stale animation leaks, and unblock the router if the abort\n          // fires before updateCallback was invoked.\n          resolveAndClear();\n          currentVT?.skipTransition?.();\n          resolveLeave();\n        },\n        { once: true },\n      );\n\n      try {\n        currentVT = document.startViewTransition(() => {\n          // Resolving here unblocks the router at the moment the browser\n          // enters updateCallback — by spec, old DOM snapshot is captured\n          // before this callback runs. Router now proceeds through\n          // activation guards and setState; the VT animation waits on\n          // `deferred`, which is resolved from router.subscribe after a\n          // task-queue tick (see NOTE on setTimeout below).\n          resolveLeave();\n\n          return deferred;\n        });\n      } catch {\n        // Defensive: spec says startViewTransition doesn't throw under\n        // normal conditions, but Chromium has had edge cases (detached\n        // document, extension interference). Clean up and unblock router.\n        resolveAndClear();\n        resolveLeave();\n      }\n    });\n  });\n\n  const offSuccess = router.subscribe(() => {\n    const resolver = closeVT;\n\n    successFired = true;\n    closeVT = null;\n\n    if (resolver === null) {\n      currentVT = null;\n    } else {\n      // The VT this resolver belongs to. If the next navigation opens a new VT\n      // in the task-queue window before this setTimeout runs, the stale\n      // resolver must NOT null the new `currentVT` — otherwise a later\n      // cancellation reads `null` and skips nothing, leaking a stale animation\n      // (#781). Same identity-guard technique the navigation-plugin uses to\n      // heal its seams.\n      const scheduledVT = currentVT;\n\n      // CRITICAL: CANNOT use requestAnimationFrame here. When the router\n      // takes the async path (leave returned a Promise), subscribe fires\n      // AFTER the browser has already transitioned VT into the\n      // \"update-callback-called\" phase. In that phase Chromium sets\n      // rendering suppression to true, which ALSO blocks rAF callbacks.\n      // rAF would never fire → deferred never resolves → browser aborts\n      // vt.ready with TimeoutError after 4s (observed in Chromium).\n      //\n      // setTimeout runs on the task queue independent of the rendering\n      // pipeline, so it fires regardless of suppression. React's scheduler\n      // uses MessageChannel tasks, which are queued before our setTimeout,\n      // so the new DOM is committed by the time our callback runs.\n      setTimeout(() => {\n        resolver();\n\n        if (currentVT === scheduledVT) {\n          currentVT = null;\n        }\n      }, 0);\n    }\n  });\n\n  return {\n    destroy: () => {\n      offLeave();\n      offSuccess();\n      currentVT?.skipTransition?.();\n      currentVT = null;\n      resolveAndClear();\n    },\n  };\n}\n","import { 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\n// A router path whose leading `/` is followed by `/` or `\\` names a host, and\n// `/.` keeps it a path on the page's origin. A copy of `HOST_PREFIX` in\n// `shared/browser-env/url-utils.ts`: the dom-utils symlink graph does not reach\n// browser-env.\nconst HOST_PREFIX = /^\\/[\\t\\n\\r]*[/\\\\]/;\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    const href = HOST_PREFIX.test(path) ? `/.${path}` : path;\n\n    return normHash ? `${href}#${encodeFragmentInline(normHash)}` : href;\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 { defineComponent, h, computed, shallowRef, watch } from \"vue\";\n\nimport { useRouter } from \"../composables/useRouter\";\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n  shouldNavigate,\n  targetsAnotherContext,\n  buildHref,\n  buildActiveClassName,\n  navigateWithHash,\n  resolveLinkTarget,\n  shallowEqual,\n} from \"../dom-utils\";\n\nimport type {\n  Params,\n  NavigationOptions,\n  NavigationTarget,\n  SearchParams,\n} from \"@real-router/core\";\nimport type { PropType } from \"vue\";\n\ntype OnClickHandler = (evt: MouseEvent) => void;\n\n/**\n * Invoke ONE user `@click` handler in isolation (#1352). A throwing handler must\n * NOT skip the Link's own navigation (which `handleClick` runs after this\n * returns) or — in the array form — abort the remaining sibling handlers. Native\n * `<a>` logs a throwing click listener and still performs the default action;\n * this matches the codebase's adapter-callback isolation norm\n * (`RouterErrorBoundary.onError`, `EventEmitter.emit`). The user's own\n * `preventDefault()` still takes effect (it runs before any throw), so the\n * `defaultPrevented` contract below is unchanged.\n */\nfunction invokeUserOnClick(fn: OnClickHandler, evt: MouseEvent): void {\n  try {\n    fn(evt);\n  } catch (error) {\n    console.error(\n      \"[real-router] A <Link> @click handler threw; navigation is unaffected.\",\n      error,\n    );\n  }\n}\n\n/**\n * Vue's compiled template binds multiple `@click` handlers as an array.\n * Single render-function `onClick` is a function. Both must be invoked, each\n * isolated via `invokeUserOnClick` (#1352).\n *\n * The function-branch deliberately omits a `defaultPrevented` check: the\n * single call short-circuits naturally and control returns to the caller\n * (`handleClick`), which then re-reads `evt.defaultPrevented` on the same\n * MouseEvent. The array-branch needs the per-iteration check because the\n * caller cannot observe intermediate handlers — without it, later handlers\n * would still run after an earlier one called `preventDefault()`.\n */\nfunction invokeAttributesOnClick(value: unknown, evt: MouseEvent): void {\n  if (typeof value === \"function\") {\n    invokeUserOnClick(value as OnClickHandler, evt);\n\n    return;\n  }\n  if (Array.isArray(value)) {\n    const handlers = value as OnClickHandler[];\n\n    for (const fn of handlers) {\n      if (typeof fn !== \"function\") {\n        continue;\n      }\n\n      invokeUserOnClick(fn, evt);\n\n      if (evt.defaultPrevented) {\n        return;\n      }\n    }\n  }\n}\n\nexport const Link = defineComponent({\n  name: \"Link\",\n  // Disable Vue's automatic attribute fallthrough. Without this, attrs.onClick\n  // (function OR array) is auto-attached as a native click listener AND our\n  // explicit onClick fires too — user handlers are double-invoked. We invoke\n  // attrs.onClick manually inside handleClick to preserve preventDefault.\n  inheritAttrs: false,\n  props: {\n    // Optional (was required) since the descriptor form (`to`) omits it — the\n    // two forms are mutually exclusive, enforced at runtime by resolveLinkTarget.\n    routeName: {\n      type: String,\n      default: undefined,\n    },\n    // Descriptor form (RFC-4 M2 B2, #1548): `to={{ name, params?, search? }}`,\n    // mutually exclusive with the routeName/routeParams/routeSearch channels.\n    to: {\n      type: Object as PropType<NavigationTarget>,\n      default: undefined,\n    },\n    // Default `undefined` (NOT EMPTY_PARAMS): an omitted `routeParams` must reach\n    // `createActiveRouteSource` as `undefined` so it keys the active source as \"\"\n    // and shares ONE cached source (one router subscription) with every other\n    // consumer asking that same question — another `<Link routeName>`, or a\n    // direct `createActiveSource(router, routeName)`. Defaulting to EMPTY_PARAMS ({}) here would key\n    // \"{}\" and split the same logical question into a second eager subscription\n    // (#776). Navigation/href default to EMPTY_PARAMS locally where a concrete\n    // object is required.\n    routeParams: {\n      type: Object as PropType<Params>,\n      default: undefined,\n    },\n    // Query (search) channel (RFC-4 M2, #1548) — parallel to routeParams.\n    routeSearch: {\n      type: Object as PropType<SearchParams>,\n      default: undefined,\n    },\n    routeOptions: {\n      type: Object as PropType<NavigationOptions>,\n      default: () => EMPTY_OPTIONS,\n    },\n    class: {\n      type: String,\n      default: undefined,\n    },\n    activeClassName: {\n      type: String,\n      default: \"active\",\n    },\n    activeStrict: {\n      type: Boolean,\n      default: false,\n    },\n    ignoreQueryParams: {\n      type: Boolean,\n      default: true,\n    },\n    target: {\n      type: String,\n      default: undefined,\n    },\n    /**\n     * URL fragment (decoded form, no leading \"#\") (#532).\n     * - omitted/`undefined` → preserve current fragment on same-route navigation\n     * - `\"\"` → clear fragment\n     * - non-empty → set fragment\n     */\n    hash: {\n      type: String,\n      default: undefined,\n    },\n  },\n  setup(props, { slots, attrs }) {\n    const router = useRouter();\n\n    const isActive = shallowRef(false);\n\n    // Content-stable `routeParams` reference. A parent that hands an inline\n    // `:routeParams=\"{ id: 1 }\"` literal allocates a fresh object on every\n    // render; keying the derivations below off that raw reference would re-run\n    // `buildHref` and re-subscribe the ActiveRouteSource on every unrelated\n    // parent update. `shallowEqual` collapses structurally-equal params to a\n    // stable reference (Object.is per key, order-insensitive — the same\n    // contract as the React adapter's Link `memo` comparator), so the `href`\n    // computed and the active `watch` bail out until params content actually\n    // changes.\n    //\n    // Hot path on Link-heavy pages: this replaces a per-navigation\n    // `canonicalJson` (JSON.stringify + key sort) with a per-navigation\n    // `shallowEqual` (no allocation), and lets same-shape navigations skip\n    // `buildHref` entirely. Nested-object param VALUES fall back to per-render\n    // recompute (shallowEqual compares them by reference) — stabilize with a\n    // `ref`/`computed` if it matters, exactly as documented for the React Link.\n    // `Params | undefined`: `undefined` (no params) is preserved end-to-end so the\n    // active source keys \"\" (see the `routeParams` prop default note, #776).\n    let cachedParams: Params | undefined = props.routeParams;\n    const stableParams = computed<Params | undefined>(() => {\n      const next = props.routeParams;\n\n      if (!shallowEqual(cachedParams, next)) {\n        cachedParams = next;\n      }\n\n      return cachedParams;\n    });\n\n    // Recreate the ActiveRouteSource ONLY when the Link's identity\n    // (routeName / params content / strict / ignoreQueryParams / hash) changes.\n    // `stableParams` already absorbs same-content param churn, so this dep\n    // array is reference-stable across navigations that don't change shape and\n    // the subscription persists.\n    watch(\n      () =>\n        [\n          props.routeName,\n          stableParams.value,\n          props.routeSearch,\n          props.to,\n          props.activeStrict,\n          props.ignoreQueryParams,\n          props.hash,\n        ] as const,\n      (\n        [\n          routeName,\n          routeParams,\n          routeSearch,\n          to,\n          activeStrict,\n          ignoreQueryParams,\n          hash,\n        ],\n        _prev,\n        onCleanup,\n      ) => {\n        // Resolve the two prop forms (RFC-4 M2 B2, #1548): `to` supersedes the\n        // channel props (dev-warn on conflict).\n        const resolved = resolveLinkTarget(\n          to,\n          routeName ?? \"\",\n          routeParams,\n          routeSearch,\n        );\n\n        // Hash-aware active (#532): pass hash through so tab links with the\n        // same routeName but different `hash` props don't all light up.\n        const source = createActiveSource(\n          router,\n          resolved.name,\n          resolved.params,\n          resolved.search,\n          activeStrict,\n          ignoreQueryParams,\n          hash,\n        );\n\n        isActive.value = source.getSnapshot();\n\n        const unsub = source.subscribe(() => {\n          isActive.value = source.getSnapshot();\n        });\n\n        onCleanup(unsub);\n      },\n      { immediate: true, flush: \"sync\" },\n    );\n\n    const href = computed(() => {\n      const resolved = resolveLinkTarget(\n        props.to,\n        props.routeName ?? \"\",\n        stableParams.value,\n        props.routeSearch,\n      );\n\n      return buildHref(\n        router,\n        resolved.name,\n        resolved.params ?? EMPTY_PARAMS,\n        resolved.search,\n        props.hash,\n      );\n    });\n\n    const finalClassName = computed(() =>\n      buildActiveClassName(isActive.value, props.activeClassName, props.class),\n    );\n\n    const handleClick = (evt: MouseEvent) => {\n      // Vue allows attrs.onClick to be a function or an array of functions\n      // (compiled templates with multiple @click bindings produce arrays).\n      // Both must be invoked; treating arrays as \"no handler\" silently drops\n      // user code.\n      if (attrs.onClick !== undefined && attrs.onClick !== null) {\n        invokeAttributesOnClick(attrs.onClick, evt);\n\n        if (evt.defaultPrevented) {\n          return;\n        }\n      }\n\n      if (!shouldNavigate(evt) || targetsAnotherContext(props.target)) {\n        return;\n      }\n\n      evt.preventDefault();\n\n      const resolved = resolveLinkTarget(\n        props.to,\n        props.routeName ?? \"\",\n        props.routeParams,\n        props.routeSearch,\n      );\n\n      navigateWithHash(\n        router,\n        resolved.name,\n        resolved.params ?? EMPTY_PARAMS,\n        resolved.search,\n        props.hash,\n        props.routeOptions,\n      ).catch(() => {});\n    };\n\n    return () => {\n      // Build forwarded attrs without `onClick`. Vue's runtime auto-attaches\n      // attrs.onClick (function OR array) as a native DOM listener, which would\n      // double-invoke user handlers when combined with our explicit `onClick`.\n      // We invoke the original attrs.onClick manually inside handleClick so the\n      // preventDefault contract is preserved.\n      //\n      // Spread + delete avoids the per-key copy loop on every render — one\n      // allocation + one property deletion instead of N iterations across\n      // data-*, aria-*, role, etc. Hot-path optimisation for Link-heavy pages.\n      const restAttributes = { ...attrs };\n\n      delete restAttributes.onClick;\n\n      return h(\n        \"a\",\n        {\n          ...restAttributes,\n          href: href.value,\n          class: finalClassName.value,\n          target: props.target,\n          onClick: handleClick,\n        },\n        slots.default?.(),\n      );\n    };\n  },\n});\n","import { createDismissableError } from \"@real-router/sources\";\nimport { defineComponent, h, watch, Fragment } from \"vue\";\n\nimport { useRouter } from \"../composables/useRouter\";\nimport { useRefFromSource } from \"../useRefFromSource\";\n\nimport type { RouterError, State } from \"@real-router/core\";\nimport type { VNode, PropType } from \"vue\";\n\nexport const RouterErrorBoundary = defineComponent({\n  name: \"RouterErrorBoundary\",\n  props: {\n    fallback: {\n      type: Function as PropType<\n        (error: RouterError, resetError: () => void) => VNode\n      >,\n      required: true,\n    },\n    onError: {\n      type: Function as PropType<\n        (\n          error: RouterError,\n          toRoute: State | null,\n          fromRoute: State | null,\n        ) => void\n      >,\n      default: undefined,\n    },\n  },\n  setup(props, { slots }) {\n    const router = useRouter();\n    const snapshot = useRefFromSource(createDismissableError(router));\n\n    watch(\n      () => snapshot.value.version,\n      () => {\n        if (snapshot.value.error) {\n          props.onError?.(\n            snapshot.value.error,\n            snapshot.value.toRoute,\n            snapshot.value.fromRoute,\n          );\n        }\n      },\n      { immediate: true },\n    );\n\n    return () => {\n      const children = slots.default?.() ?? [];\n      const errorVNode = snapshot.value.error\n        ? props.fallback(snapshot.value.error, snapshot.value.resetError)\n        : null;\n\n      return h(Fragment, null, [...children, errorVNode]);\n    };\n  },\n});\n\nexport type RouterErrorBoundaryProps = InstanceType<\n  typeof RouterErrorBoundary\n>[\"$props\"];\n","import {\n  shouldNavigate,\n  anchorTargetsAnotherContext,\n  applyLinkA11y,\n} from \"../dom-utils\";\n\nimport type { Router, NavigationOptions, Params } from \"@real-router/core\";\nimport type { Directive } from \"vue\";\n\nexport interface LinkDirectiveValue {\n  name: string;\n  params?: Params;\n  options?: NavigationOptions;\n}\n\n/**\n * Router stack for nested RouterProviders. The active router is the top of\n * the stack. RouterProvider pushes its router on mount and pops it on unmount,\n * which preserves the parent context when an inner provider tears down.\n *\n * Without the stack, an unmounted child provider would leave the directive\n * pointing at a disposed router, and v-link in the still-mounted parent would\n * navigate via the wrong (or torn-down) instance.\n */\nconst routerStack: Router[] = [];\n\n/**\n * Pushes a router onto the active stack. Returns a release function that\n * removes that exact router from the stack regardless of position — safe\n * across out-of-order provider unmount sequences.\n *\n * @internal Used by RouterProvider during setup/teardown.\n */\nexport function pushDirectiveRouter(router: Router): () => void {\n  routerStack.push(router);\n\n  return () => {\n    const idx = routerStack.lastIndexOf(router);\n\n    if (idx !== -1) {\n      routerStack.splice(idx, 1);\n    }\n  };\n}\n\n/**\n * Backwards-compatible alias. Replaces the active router unconditionally and\n * does NOT participate in the stack — use {@link pushDirectiveRouter} from\n * provider code instead. Not exported from the package entry; retained for\n * unit tests and rare standalone-directive setups (where v-link is mounted\n * outside any RouterProvider).\n *\n * @internal\n */\nexport function setDirectiveRouter(router: Router | null): void {\n  if (router === null) {\n    routerStack.length = 0;\n\n    return;\n  }\n  if (routerStack.length === 0) {\n    routerStack.push(router);\n\n    return;\n  }\n\n  routerStack[routerStack.length - 1] = router;\n}\n\nexport function getDirectiveRouter(): Router {\n  const top = routerStack.at(-1);\n\n  if (!top) {\n    throw new Error(\n      \"v-link directive requires a RouterProvider ancestor. Make sure RouterProvider is mounted.\",\n    );\n  }\n\n  return top;\n}\n\ninterface Handlers {\n  click: (evt: MouseEvent) => void;\n  keydown: (evt: KeyboardEvent) => void;\n}\n\n// Single WeakMap halves per-element bookkeeping vs two parallel maps.\nconst handlers = new WeakMap<HTMLElement, Handlers>();\n\n/**\n * Validates a directive binding value before attaching handlers.\n * Returns false (and warns once per call) when the value is missing or\n * has no `name` — silently doing nothing is preferable to a runtime crash\n * inside a click handler.\n */\nfunction isValidBinding(value: unknown): value is LinkDirectiveValue {\n  if (value === null || value === undefined) {\n    console.error(\n      \"[real-router] v-link directive received null/undefined value. The element will not be wired for navigation.\",\n    );\n\n    return false;\n  }\n  if (typeof (value as { name?: unknown }).name !== \"string\") {\n    console.error(\n      \"[real-router] v-link directive value is missing a string `name` field. The element will not be wired for navigation.\",\n    );\n\n    return false;\n  }\n\n  return true;\n}\n\nfunction createClickHandler(\n  router: Router,\n  value: LinkDirectiveValue,\n  element: HTMLElement,\n): (evt: MouseEvent) => void {\n  return (evt: MouseEvent) => {\n    if (!shouldNavigate(evt) || anchorTargetsAnotherContext(element)) {\n      return;\n    }\n\n    evt.preventDefault();\n    router\n      .navigate(value.name, value.params ?? {}, undefined, value.options ?? {})\n      .catch(() => {});\n  };\n}\n\nfunction createKeydownHandler(\n  router: Router,\n  value: LinkDirectiveValue,\n  element: HTMLElement,\n): (evt: KeyboardEvent) => void {\n  return (evt: KeyboardEvent) => {\n    // Enter is an activation like the click above, and the anchor's `target`\n    // scopes it the same way: the browser is already loading the URL into the\n    // context the markup named, so a second, in-app navigation would move the\n    // page out from under it (#1834).\n    if (\n      evt.key === \"Enter\" &&\n      !(element instanceof HTMLButtonElement) &&\n      !anchorTargetsAnotherContext(element)\n    ) {\n      router\n        .navigate(\n          value.name,\n          value.params ?? {},\n          undefined,\n          value.options ?? {},\n        )\n        .catch(() => {});\n    }\n  };\n}\n\nfunction attachHandlers(\n  element: HTMLElement,\n  router: Router,\n  value: LinkDirectiveValue,\n): void {\n  const click = createClickHandler(router, value, element);\n  const keydown = createKeydownHandler(router, value, element);\n\n  element.addEventListener(\"click\", click);\n  element.addEventListener(\"keydown\", keydown);\n\n  handlers.set(element, { click, keydown });\n}\n\nfunction detachHandlers(element: HTMLElement): void {\n  const entry = handlers.get(element);\n\n  if (entry) {\n    element.removeEventListener(\"click\", entry.click);\n    element.removeEventListener(\"keydown\", entry.keydown);\n    handlers.delete(element);\n  }\n}\n\nexport const vLink: Directive<HTMLElement, LinkDirectiveValue> = {\n  mounted(element, binding) {\n    const router = getDirectiveRouter();\n\n    applyLinkA11y(element);\n\n    element.style.cursor = \"pointer\";\n\n    if (!isValidBinding(binding.value)) {\n      return;\n    }\n\n    attachHandlers(element, router, binding.value);\n  },\n\n  updated(element, binding) {\n    // Hot-path guard: Vue invokes `updated` on every parent re-render even\n    // when the directive's binding value reference has not changed. Without\n    // this short-circuit, every parent rerender (which is the common case on\n    // Link-heavy pages — any unrelated state change triggers the parent's\n    // render fn) would detach + reattach the click/keydown listeners.\n    // Comparing references is enough: when consumers pass a stable\n    // `LinkDirectiveValue` object (the recommended pattern, since Vue's\n    // template compiler hoists `v-link=\"{ name: 'home' }\"` to a stable\n    // literal), this guard collapses the work to zero.\n    if (binding.value === binding.oldValue) {\n      return;\n    }\n\n    const router = getDirectiveRouter();\n\n    detachHandlers(element);\n\n    if (!isValidBinding(binding.value)) {\n      return;\n    }\n\n    attachHandlers(element, router, binding.value);\n  },\n\n  beforeUnmount(element) {\n    detachHandlers(element);\n  },\n};\n","import { inject } from \"vue\";\n\nimport { NavigatorKey } from \"../context\";\n\nimport type { Navigator } from \"@real-router/core\";\n\nexport const useNavigator = (): Navigator => {\n  const navigator = inject(NavigatorKey);\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\nexport const useRouteUtils = (): RouteUtils => {\n  const router = useRouter();\n\n  return getRouteUtils(getPluginApi(router).getTree());\n};\n","import { getTransitionSource } from \"@real-router/sources\";\n\nimport { useRefFromSource } from \"../useRefFromSource\";\nimport { useRouter } from \"./useRouter\";\n\nimport type { RouterTransitionSnapshot } from \"@real-router/sources\";\nimport type { ShallowRef } from \"vue\";\n\nexport function useRouterTransition(): ShallowRef<RouterTransitionSnapshot> {\n  const router = useRouter();\n  const source = getTransitionSource(router);\n\n  return useRefFromSource(source);\n}\n","import { guardLeaveListener } from \"@real-router/sources\";\nimport { onActivated, onDeactivated, onScopeDispose } from \"vue\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { State } from \"@real-router/core\";\n\nexport interface RouteExitContext {\n  /** The route being left. */\n  route: State;\n  /** The route being navigated to. */\n  nextRoute: State;\n  /**\n   * AbortSignal that fires when this navigation is superseded by a later\n   * one (rapid clicks). Already filtered: when the handler runs,\n   * `signal.aborted` is guaranteed to be `false`. Use\n   * `signal.addEventListener(\"abort\", cleanup, { once: true })` for\n   * cleanup that must run on cancellation.\n   */\n  signal: AbortSignal;\n}\n\nexport interface UseRouteExitOptions {\n  /**\n   * Skip the handler when `route.name === nextRoute.name`\n   * (sort/filter/query-only navigations on the same route). Default:\n   * `true`.\n   */\n  skipSameRoute?: boolean;\n}\n\nexport type RouteExitHandler = (\n  context: RouteExitContext,\n) => void | Promise<void>;\n\n/**\n * Subscribe to the router's leave-window with the universal guards baked\n * in. Wraps `router.subscribeLeave` so consumers don't repeat the same\n * boilerplate every time:\n *\n *   - **Reentrant abort pre-check**: if `signal.aborted` is already `true`\n *     when the handler would run (rapid navigation superseded a slower\n *     one), the handler is skipped entirely.\n *   - **Same-route skip**: by default, `route.name === nextRoute.name`\n *     short-circuits the handler — query-only navigations skip the work.\n *     Opt out with `skipSameRoute: false`.\n *\n * Cleanup is bound to the component's effect scope via `onScopeDispose`.\n *\n * If the handler returns a Promise, the router blocks on it. If the\n * Promise resolves, navigation proceeds. If it **rejects**, the router\n * rejects `navigate()` with the handler's **original error** (a value that\n * is not an `Error` arrives wrapped in one) and emits `TRANSITION_ERROR` —\n * it is NOT re-coded to `TRANSITION_CANCELLED` (that arises when the\n * navigation's `signal` aborts). A handler that rejects with a\n * `RouterError(TRANSITION_CANCELLED)` itself cancels the navigation quietly:\n * `navigate()` rejects with that error and the router emits\n * `TRANSITION_CANCEL` instead (#2787).\n *\n * **Handler reactivity (Vue):** Vue composables run **once** during\n * `setup()`; `handler` is captured in closure at the call site. To vary\n * behavior over time, read refs/computeds inside the handler body — do\n * not rely on swapping the handler reference.\n *\n * **Under `<KeepAlive>` (#1221):** a deactivated (sleeping) page does not run\n * the handler on unrelated navigations — critically, its async exit is not\n * spliced into every navigation's leave cycle (where it would block the app).\n *\n * @example Animation\n * ```ts\n * const ref = useTemplateRef<HTMLDivElement>(\"box\");\n *\n * useRouteExit(async ({ signal }) => {\n *   const el = ref.value;\n *   if (!el) return;\n *   el.classList.add(\"fade-out\");\n *   const cleanup = () => el.classList.remove(\"fade-out\");\n *   signal.addEventListener(\"abort\", cleanup, { once: true });\n *   try {\n *     el.getBoundingClientRect();\n *     await Promise.allSettled(el.getAnimations().map((a) => a.finished));\n *   } finally {\n *     cleanup();\n *   }\n * });\n * ```\n *\n * @example Auto-save form draft\n * ```ts\n * useRouteExit(async ({ signal }) => {\n *   if (formState.value.dirty) {\n *     await api.saveDraft(formState.value, { signal });\n *   }\n * });\n * ```\n *\n * @example Detecting that you are leaving a subtree\n * ```ts\n * const inProducts = (name: string) =>\n *   name === \"products\" || name.startsWith(\"products.\");\n *\n * useRouteExit(({ route, nextRoute }) => {\n *   if (inProducts(route.name) && !inProducts(nextRoute.name)) {\n *     // leaving the products subtree entirely — flush product-related caches\n *     productCache.clear();\n *   }\n * });\n * ```\n *\n * ⚠ Do NOT read `nextRoute.transition` here. `nextRoute` is the PENDING target,\n * and the pipeline gives it the neutral default — empty `segments`, optional\n * flags `undefined` — so the example this replaced read `[]` and `undefined`\n * whatever the navigation was. (It threw outright until real-router#1976\n * attached the field.) The real metadata is written at the COMMIT: read it in\n * `router.subscribe`, `onTransitionSuccess`, or `getState()`.\n */\nexport function useRouteExit(\n  handler: RouteExitHandler,\n  options?: UseRouteExitOptions,\n): void {\n  const router = useRouter();\n  const skipSameRoute = options?.skipSameRoute ?? true;\n\n  // #1221 — under <KeepAlive> a deactivated component keeps its effect scope\n  // (and this subscription) alive. Gate on deactivated state so a sleeping page\n  // does not run its exit handler on unrelated navigations — critically, a\n  // sleeping page's async (Promise-returning) exit would otherwise be spliced\n  // into every navigation's leave cycle and BLOCK it. `onActivated` /\n  // `onDeactivated` only fire under KeepAlive; without it the flag stays `false`\n  // and the subscription runs as before. The page being LEFT is still active\n  // when its own leave window runs (deactivation happens on the subsequent\n  // commit), so a genuine exit still fires.\n  let isDeactivated = false;\n\n  onActivated(() => {\n    isDeactivated = false;\n  });\n  onDeactivated(() => {\n    isDeactivated = true;\n  });\n\n  // The same-route + reentrant-abort guards and the Promise passthrough live in\n  // the shared listener (@real-router/sources, #1435); the handler is captured\n  // at init (Vue's setup runs once).\n  const guardedLeave = guardLeaveListener(handler, { skipSameRoute });\n\n  const off = router.subscribeLeave((leaveState) => {\n    // The #1221 KeepAlive pre-gate stays adapter-side and composes BEFORE the\n    // shared guards — a deactivated (sleeping) page must never enter the leave\n    // cycle (its async exit would otherwise be spliced into every navigation and\n    // block it).\n    if (isDeactivated) {\n      return;\n    }\n\n    return guardedLeave(leaveState);\n  });\n\n  onScopeDispose(off);\n}\n","import { createRouteEnterGate } from \"@real-router/sources\";\nimport { onActivated, onDeactivated, watch } from \"vue\";\n\nimport { useRoute } from \"./useRoute\";\n\nimport type { State } from \"@real-router/core\";\n\nexport interface RouteEnterContext {\n  /** The route that was just activated. */\n  route: State;\n  /** The route that was active immediately before this navigation. */\n  previousRoute: State;\n}\n\nexport type RouteEnterHandler = (context: RouteEnterContext) => void;\n\nexport interface UseRouteEnterOptions {\n  /**\n   * Skip the handler when `route.name === previousRoute.name`\n   * (sort/filter/query-only navigations on the same route). Default:\n   * `true`. Symmetric with `useRouteExit`'s same-name option.\n   */\n  skipSameRoute?: boolean;\n}\n\n/**\n * Fire `handler` once when the component mounts as a result of a\n * navigation. Mirror of `useRouteExit` for the entry side.\n *\n * What this composable covers that an ad-hoc `watch` + `useRoute()`\n * doesn't:\n *\n *   - **Skip-initial**: `watch` (with default `immediate: false`) plus the\n *     explicit `route.transition.from` guard ensures the handler doesn't\n *     fire on the very first state committed by `router.start()`.\n *   - **Same-route skip** (default): handler is skipped when\n *     `route.transition.from === route.name`. Sort/filter/query-only\n *     navigations re-trigger the watcher, but they are not \"entries\" in\n *     the animation / analytics sense. Opt out with\n *     `skipSameRoute: false`.\n *   - **Mount-time `route` / `previousRoute` snapshot**: handler receives\n *     the values that were live at the moment of the new state, not the\n *     latest ones (which may have moved on if the user navigated again\n *     before the watcher drained).\n *\n * **Handler reactivity (Vue):** Vue composables run **once** during\n * `setup()`; `handler` is captured in closure at the call site. To vary\n * behavior over time, read refs/computeds inside the handler body.\n *\n * **Under `<KeepAlive>` (#1221):** a deactivated (sleeping) page does not fire\n * the handler on unrelated navigations, and waking it does not re-fire —\n * reactivation is not a mount (use Vue's native `onActivated` to re-run on show).\n *\n * @example Direction-aware entry animation\n * ```ts\n * useRouteEnter(({ route }) => {\n *   const direction = route.context.browser?.direction;\n *   ref.value?.classList.add(\n *     direction === \"back\" ? \"slide-from-left\" : \"slide-from-right\",\n *   );\n * });\n * ```\n *\n * @example Analytics page-enter event (skip-initial built-in)\n * ```ts\n * useRouteEnter(({ route, previousRoute }) => {\n *   analytics.track(\"page_enter\", {\n *     route: route.name,\n *     from: previousRoute.name,\n *   });\n * });\n * ```\n *\n * @example Reading rich transition metadata via `route.transition`\n * ```ts\n * useRouteEnter(({ route }) => {\n *   // ⚠ NOT `transition.redirected` — the router never sets it (it only ever\n *   // carries what a caller passed as `{ redirected: true }`), so branching on\n *   // it here is silently dead for a `forwardTo` or a guard redirect.\n *   // `from` needs no check: this hook does not fire without one.\n *   showToast(`Arrived from ${route.transition.from}`);\n * });\n * ```\n */\nexport function useRouteEnter(\n  handler: RouteEnterHandler,\n  options?: UseRouteEnterOptions,\n): void {\n  const { route, previousRoute } = useRoute();\n  const skipSameRoute = options?.skipSameRoute ?? true;\n  // The canonical enter-guard set + `lastHandledRoute` dedupe live in the shared\n  // gate (@real-router/sources, #1435). Vue's setup runs once, so a plain const\n  // holds the gate across watcher runs. The gate owns skip-initial / same-route\n  // / dedupe / the `!previousRoute` guard — the sole defense of the non-nullable\n  // `RouteEnterContext.previousRoute` contract (#1218), now tested once in\n  // sources rather than v8-ignored per adapter. (In Vue both skip-initial and\n  // `!prev` are in fact unreachable — `watch` defaults to `immediate: false`, so\n  // the initial commit and the undefined-`previousRoute` baseline are never\n  // observed — but the gate arms them uniformly for cross-adapter parity.)\n  const gate = createRouteEnterGate();\n\n  // #1221 — under <KeepAlive> a deactivated component keeps its effect scope\n  // (and this watcher) alive, so a sleeping page would otherwise fire `handler`\n  // on every unrelated app navigation. This pre-gate stays adapter-side: the\n  // framework-free gate has no channel to observe Vue's deactivation lifecycle.\n  // `onActivated` / `onDeactivated` only fire under KeepAlive; without it the\n  // flag stays `false` and the watcher runs exactly as before. Reactivating a\n  // kept-alive page does NOT re-fire enter (strict-mount): this watcher flushes\n  // before `onActivated`, so the flag is still `true` when the reactivating\n  // navigation lands — waking a never-unmounted page is not a mount.\n  let isDeactivated = false;\n\n  onActivated(() => {\n    isDeactivated = false;\n  });\n  onDeactivated(() => {\n    isDeactivated = true;\n  });\n\n  watch(route, (newRoute) => {\n    if (isDeactivated) {\n      return;\n    }\n\n    const context = gate(newRoute, previousRoute.value, skipSameRoute);\n\n    if (context) {\n      handler(context);\n    }\n  });\n}\n","// packages/vue/src/setupRouteProvision.ts\n\nimport { getNavigator } from \"@real-router/core\";\nimport { createRouteSource } from \"@real-router/sources\";\nimport { shallowRef } from \"vue\";\n\nimport type { Router, Navigator, State } from \"@real-router/core\";\nimport type { ShallowRef } from \"vue\";\n\n/**\n * Shared setup for `RouterProvider` (component-scoped) and\n * `createRouterPlugin` (app-scoped). Builds the reactive route refs +\n * subscription bookkeeping in one place; callers wire the result into their\n * provide/inject mechanism and own teardown lifecycle.\n *\n * @internal\n */\nexport interface RouteProvision {\n  navigator: Navigator;\n  route: ShallowRef<State | undefined>;\n  previousRoute: ShallowRef<State | undefined>;\n  /** Call when the owning scope tears down to release the router subscription. */\n  unsubscribe: () => void;\n}\n\nexport function setupRouteProvision(router: Router): RouteProvision {\n  const navigator = getNavigator(router);\n  const source = createRouteSource(router);\n  const initial = source.getSnapshot();\n\n  const route = shallowRef<State | undefined>(initial.route);\n  const previousRoute = shallowRef<State | undefined>(initial.previousRoute);\n\n  const unsubscribe = source.subscribe(() => {\n    const snapshot = source.getSnapshot();\n\n    route.value = snapshot.route;\n    previousRoute.value = snapshot.previousRoute;\n  });\n\n  return { navigator, route, previousRoute, unsubscribe };\n}\n","import { NavigatorKey, RouteKey, RouterKey } from \"./context\";\nimport { pushDirectiveRouter } from \"./directives/vLink\";\nimport { setupRouteProvision } from \"./setupRouteProvision\";\n\nimport type { Router } from \"@real-router/core\";\nimport type { App, Plugin } from \"vue\";\n\nexport function createRouterPlugin(router: Router): Plugin<[]> {\n  return {\n    install(app): void {\n      const releaseDirective = pushDirectiveRouter(router);\n\n      const { navigator, route, previousRoute, unsubscribe } =\n        setupRouteProvision(router);\n\n      // Vue 3.5+ exposes app.onUnmount for plugin cleanup.\n      // On older versions (3.3–3.4), the subscription is cleaned up\n      // when the router is garbage-collected (same as vue-router).\n      if (\"onUnmount\" in app) {\n        (app as App & { onUnmount: (fn: () => void) => void }).onUnmount(() => {\n          releaseDirective();\n          unsubscribe();\n        });\n      }\n\n      app.provide(RouterKey, router);\n      app.provide(NavigatorKey, navigator);\n      app.provide(RouteKey, { navigator, route, previousRoute });\n    },\n  };\n}\n","import { primeErrorSource } from \"@real-router/sources\";\nimport { defineComponent, onScopeDispose, provide, watch } from \"vue\";\n\nimport { NavigatorKey, RouteKey, RouterKey } from \"./context\";\nimport { pushDirectiveRouter } from \"./directives/vLink\";\nimport {\n  createRouteAnnouncer,\n  createScrollRestoration,\n  createScrollSpy,\n  createViewTransitions,\n} from \"./dom-utils\";\nimport { setupRouteProvision } from \"./setupRouteProvision\";\n\nimport type {\n  RouteAnnouncerOptions,\n  ScrollRestorationOptions,\n  ScrollSpyOptions,\n} from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { PropType } from \"vue\";\n\ninterface Disposable {\n  destroy: () => void;\n}\n\n/**\n * Watch a dependency tuple and (re)create a toggleable utility (announcer /\n * scroll-restorer / view-transitions). The factory returns `undefined` to\n * mean \"feature disabled\" — no utility is created and no cleanup is wired.\n * When a utility IS returned, its `destroy()` is registered via `onCleanup`,\n * so flipping any dep (incl. the feature flag) tears down the previous\n * instance before constructing the next.\n *\n * Extracted from three near-identical `watch(... { immediate: true })` blocks\n * (announceNavigation / scrollRestoration / viewTransitions) — DRY without\n * losing the per-utility dep tuple shape.\n */\nfunction watchToggleableUtility<D extends readonly unknown[]>(\n  deps: () => D,\n  factory: (current: D) => Disposable | undefined,\n): void {\n  watch(\n    deps,\n    (current, _prev, onCleanup) => {\n      const utility = factory(current);\n\n      if (utility) {\n        onCleanup(() => {\n          utility.destroy();\n        });\n      }\n    },\n    { immediate: true },\n  );\n}\n\nexport const RouterProvider = defineComponent({\n  name: \"RouterProvider\",\n  props: {\n    router: {\n      type: Object as PropType<Router>,\n      required: true,\n    },\n    announceNavigation: {\n      type: [Boolean, Object] as PropType<boolean | RouteAnnouncerOptions>,\n      default: false,\n    },\n    scrollRestoration: {\n      type: Object as PropType<ScrollRestorationOptions>,\n    },\n    scrollSpy: {\n      type: Object as PropType<ScrollSpyOptions>,\n    },\n    viewTransitions: {\n      type: Boolean,\n      default: false,\n    },\n  },\n  setup(props, { slots }) {\n    // Reactive announceNavigation: setting prop true/false at runtime\n    // creates/destroys the announcer accordingly. Prior implementation read\n    // the prop only inside onMounted, so toggling it post-mount silently no-op'd.\n    // `announceNavigation` accepts `true` (default announcer) or a\n    // `RouteAnnouncerOptions` object (`{ prefix, getAnnouncementText }`) for\n    // custom announcement text. `false` / `undefined` disables it. Watched by\n    // primitives (enabled + prefix) so inline-object identity churn doesn't\n    // re-create the announcer; getAnnouncementText is captured once by the\n    // utility (same rationale as scrollContainer below).\n    watchToggleableUtility(\n      () => {\n        const nav = props.announceNavigation;\n        const enabled = nav !== false;\n        const prefix = typeof nav === \"object\" ? nav.prefix : undefined;\n\n        return [props.router, enabled, prefix] as const;\n      },\n      ([router, enabled]) => {\n        const nav = props.announceNavigation;\n        const options = typeof nav === \"object\" ? nav : undefined;\n\n        return enabled ? createRouteAnnouncer(router, options) : undefined;\n      },\n    );\n\n    // Watch by primitives so inline `{ mode: \"restore\" }` doesn't thrash.\n    // scrollContainer is a getter invoked lazily on every event inside the\n    // utility — swapping its reference doesn't change the resolved element,\n    // so we intentionally omit it from watched sources.\n    watchToggleableUtility(\n      () =>\n        [\n          props.router,\n          props.scrollRestoration !== undefined,\n          props.scrollRestoration?.mode,\n          props.scrollRestoration?.anchorScrolling,\n          props.scrollRestoration?.behavior,\n          props.scrollRestoration?.storageKey,\n        ] as const,\n      ([router, enabled, mode, anchorScrolling, behavior, storageKey]) => {\n        if (!enabled) {\n          return;\n        }\n\n        return createScrollRestoration(router, {\n          mode,\n          anchorScrolling,\n          behavior,\n          storageKey,\n          scrollContainer: props.scrollRestoration?.scrollContainer,\n        });\n      },\n    );\n\n    // Reactive scrollSpy: watch by primitives, omit scrollContainer getter\n    // identity for the same reason scrollRestoration does.\n    watchToggleableUtility(\n      () =>\n        [\n          props.router,\n          props.scrollSpy !== undefined && props.scrollSpy.selector !== \"\",\n          props.scrollSpy?.selector,\n          props.scrollSpy?.rootMargin,\n        ] as const,\n      ([router, enabled, selector, rootMargin]) => {\n        if (!enabled || !selector) {\n          return;\n        }\n\n        return createScrollSpy(router, {\n          selector,\n          rootMargin,\n          scrollContainer: props.scrollSpy?.scrollContainer,\n        });\n      },\n    );\n\n    // Reactive viewTransitions: toggling prop creates/destroys the utility.\n    watchToggleableUtility(\n      () => [props.router, props.viewTransitions] as const,\n      ([router, enabled]) =>\n        enabled ? createViewTransitions(router) : undefined,\n    );\n\n    // Push this provider's router on the v-link directive stack so nested\n    // RouterProviders behave like nested DI scopes (LIFO). Release on unmount\n    // restores the outer router for any v-link still mounted in the parent.\n    //\n    // #779: only push in the browser. The v-link directive runs solely in\n    // mounted client DOM, so the server never reads the stack — while\n    // onScopeDispose (the release hook) never fires during renderToString, so a\n    // server-side push would strand every per-request router in this\n    // module-level array, a leak router.dispose() cannot clear. `typeof\n    // document` mirrors the SSR guard used across shared/dom-utils. The release\n    // gets its own onScopeDispose so the server path leaves no dead teardown\n    // branch (a combined callback would only ever run on the client).\n    const releaseDirective =\n      typeof document === \"undefined\"\n        ? undefined\n        : pushDirectiveRouter(props.router);\n\n    if (releaseDirective) {\n      onScopeDispose(releaseDirective);\n    }\n\n    // #778 P2: eagerly create the per-router error source at Provider mount so a\n    // navigation error that fires BEFORE a RouterErrorBoundary mounts (a lazy app\n    // shell, a failed boot navigation) is still captured. The boundary's\n    // createDismissableError reuses this cached source and catches up (#765);\n    // without it the error source is created lazily on boundary mount — after the\n    // error — and never sees it.\n    primeErrorSource(props.router);\n\n    const { navigator, route, previousRoute, unsubscribe } =\n      setupRouteProvision(props.router);\n\n    onScopeDispose(unsubscribe);\n\n    provide(RouterKey, props.router);\n    provide(NavigatorKey, navigator);\n    provide(RouteKey, { navigator, route, previousRoute });\n\n    return () => slots.default?.();\n  },\n});\n"],"mappings":"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"}