{"version":3,"file":"RouterProvider-BDUET4Ke.mjs","names":["NOOP_INSTANCE","NOOP_INSTANCE","NOOP_INSTANCE"],"sources":["../../../../shared/dom-utils/route-announcer.ts","../../../../shared/dom-utils/scroll-restore.ts","../../../../shared/dom-utils/scroll-spy.ts","../../../../shared/dom-utils/view-transitions.ts","../../src/components/Link.tsx","../../src/RouterProvider.tsx"],"sourcesContent":["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 isInitialNavigation = true;\n  let isReady = false;\n  let isDestroyed = false;\n  let lastAnnouncedText = \"\";\n  let pendingText: string | null = null;\n  let clearTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n  const { 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  const unsubscribe = router.subscribe(({ route }) => {\n    if (isInitialNavigation) {\n      isInitialNavigation = false;\n\n      return;\n    }\n\n    // Double rAF: waits for two paint frames so the incoming route's DOM\n    // (including the new <h1>) is fully rendered before resolveText reads it.\n    // Single rAF fires before the new route's template has been attached,\n    // which would cause resolveText to pick up the OLD h1 or fall back to\n    // document.title / route.name prematurely.\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        if (isDestroyed) {\n          return;\n        }\n\n        const h1 = document.querySelector<HTMLElement>(\"h1\");\n        const text = resolveText(route, prefix, getCustomText, h1);\n\n        if (!text || text === lastAnnouncedText) {\n          return;\n        }\n\n        if (!isReady) {\n          // Defer announcement until Safari-ready window elapses (see safariTimeoutId).\n          pendingText = text;\n\n          return;\n        }\n\n        doAnnounce(text, h1);\n      });\n    });\n  });\n\n  return {\n    destroy() {\n      // 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 { memo, useMemo } from \"react\";\n\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\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { LinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\nfunction areLinkPropsEqual(\n  prev: Readonly<LinkProps>,\n  next: Readonly<LinkProps>,\n): boolean {\n  return (\n    prev.routeName === next.routeName &&\n    prev.className === next.className &&\n    prev.activeClassName === next.activeClassName &&\n    prev.activeStrict === next.activeStrict &&\n    prev.ignoreQueryParams === next.ignoreQueryParams &&\n    prev.onClick === next.onClick &&\n    prev.target === next.target &&\n    prev.style === next.style &&\n    prev.children === next.children &&\n    prev.hash === next.hash &&\n    shallowEqual(prev.routeParams, next.routeParams) &&\n    shallowEqual(prev.routeSearch, next.routeSearch) &&\n    shallowEqual(prev.to, next.to) &&\n    shallowEqual(prev.routeOptions, next.routeOptions)\n  );\n}\n\nconst LinkImpl: FC<LinkProps> = ({\n  routeName,\n  routeParams,\n  routeSearch,\n  to,\n  routeOptions = EMPTY_OPTIONS,\n  className,\n  activeClassName = \"active\",\n  activeStrict = false,\n  ignoreQueryParams = true,\n  hash,\n  onClick,\n  target,\n  children,\n  ...props\n}) => {\n  // memo + areLinkPropsEqual guarantees that on bail-out the component does\n  // not render; on render, routeParams/routeOptions either changed reference\n  // (true change) or comparator failed (e.g., BigInt fallback to identity),\n  // so they're safe to use directly in hook deps.\n\n  const router = useRouter();\n\n  // Resolve the two prop forms into one channel triple (RFC-4 M2 B2, #1548):\n  // a `to` descriptor supersedes the channel props (dev-warn on conflict). The\n  // `routeName ?? \"\"` keeps a descriptor-form Link (no `routeName`) on the\n  // canonical empty-name path that `buildHref` / `isActiveRoute` already handle.\n  const { name, params, search } = resolveLinkTarget(\n    to,\n    routeName ?? \"\",\n    routeParams,\n    routeSearch,\n  );\n\n  // Pass `params` straight through (possibly `undefined`) — do NOT default\n  // to EMPTY_PARAMS before the active-route call. `createActiveRouteSource` keys\n  // params as `params === undefined ? \"\" : canonicalJson(params)`, so a no-params\n  // `<Link>` and a manual `useIsActiveRoute(routeName)` both key \"\" and share ONE\n  // cached source (one router subscription). Defaulting to EMPTY_PARAMS ({}) here\n  // would key \"{}\" and split the same logical question into a second eager\n  // subscription (#776). `shallowEqual(undefined, undefined)` keeps the memo\n  // fast-path, so the comparison behaviour is unchanged.\n  //\n  // When `hash` prop is set, active state requires both route AND hash to\n  // match (#532). Without this, three tab links sharing routeName=\"settings\"\n  // would all be marked active by route-name alone, defeating tab semantics.\n  const isActive = useIsActiveRoute(\n    name,\n    params,\n    search,\n    activeStrict,\n    ignoreQueryParams,\n    hash,\n  );\n\n  // Navigation/href building need a concrete params object — default here only.\n  // `search` stays raw (`undefined` when unset): buildHref / navigateWithHash\n  // pass it straight to the query slot, which tolerates `undefined`.\n  const paramsForNav = params ?? EMPTY_PARAMS;\n\n  const href = buildHref(router, name, paramsForNav, search, hash);\n\n  // useCallback was wasteful: 7 deps recreated the closure on every meaningful\n  // render anyway, and `<a onClick>` does not benefit from a stable function\n  // identity (no child-memo-bail-out chain past it). Inline arrow function is\n  // what React Compiler emits automatically for this shape.\n  const handleClick = (evt: MouseEvent<HTMLAnchorElement>) => {\n    if (onClick) {\n      // Isolate a throwing user handler (#1436): native <a> logs a throwing\n      // click listener and still performs the default action. Without this the\n      // throw escapes before navigateWithHash, silently aborting navigation.\n      // The user's own preventDefault() runs before any throw, so the\n      // defaultPrevented contract below is unchanged. Mirrors vue's #1352.\n      try {\n        onClick(evt);\n      } catch (error) {\n        console.error(\n          \"[real-router] A <Link> onClick handler threw; navigation is unaffected.\",\n          error,\n        );\n      }\n\n      if (evt.defaultPrevented) {\n        return;\n      }\n    }\n\n    if (!shouldNavigate(evt.nativeEvent) || targetsAnotherContext(target)) {\n      return;\n    }\n\n    evt.preventDefault();\n    navigateWithHash(\n      router,\n      name,\n      paramsForNav,\n      search,\n      hash,\n      routeOptions,\n    ).catch(() => {});\n  };\n\n  // Memoize the joined class string. parseTokens + Set + join on every render\n  // adds up on pages with N Links navigating frequently; deps cover every\n  // input the function reads so cache invalidation is exact.\n  const finalClassName = useMemo(\n    () => buildActiveClassName(isActive, activeClassName, className),\n    [isActive, activeClassName, className],\n  );\n\n  return (\n    <a\n      {...props}\n      target={target}\n      href={href}\n      className={finalClassName}\n      onClick={handleClick}\n    >\n      {children}\n    </a>\n  );\n};\n\nexport const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);\n\nLink.displayName = \"Link\";\n","import { useEffect } from \"react\";\n\nimport {\n  createRouteAnnouncer,\n  createScrollRestoration,\n  createScrollSpy,\n  createViewTransitions,\n} from \"./dom-utils\";\nimport { RouterProviderCore } from \"./RouterProviderCore\";\n\nimport type {\n  RouteAnnouncerOptions,\n  ScrollRestorationOptions,\n  ScrollSpyOptions,\n} from \"./dom-utils\";\nimport type { Router } from \"@real-router/core\";\nimport type { FC, ReactNode } from \"react\";\n\nexport interface RouteProviderProps {\n  router: Router;\n  children: ReactNode;\n  announceNavigation?: boolean | RouteAnnouncerOptions;\n  scrollRestoration?: ScrollRestorationOptions;\n  scrollSpy?: ScrollSpyOptions;\n  viewTransitions?: boolean;\n}\n\n/**\n * DOM-aware router provider: wraps {@link RouterProviderCore} (contexts +\n * subscription wiring) and layers the opt-in DOM-feature effects — announcer,\n * scroll restoration, scroll spy, view transitions — on top. The factory\n * imports live here (not in the core) so terminal targets that compose only the\n * core never pull the dom-utils implementation into their chunk (#800).\n */\nexport const RouterProvider: FC<RouteProviderProps> = ({\n  router,\n  children,\n  announceNavigation,\n  scrollRestoration,\n  scrollSpy,\n  viewTransitions,\n}) => {\n  // `announceNavigation` accepts `true` (default announcer) or a\n  // `RouteAnnouncerOptions` object (`{ prefix, getAnnouncementText }`) for\n  // custom announcement text. `false` / `undefined` disables it.\n  const announceEnabled =\n    announceNavigation !== undefined && announceNavigation !== false;\n  const announceOptions =\n    typeof announceNavigation === \"object\" ? announceNavigation : undefined;\n  const announcePrefix = announceOptions?.prefix;\n\n  useEffect(() => {\n    if (!announceEnabled) {\n      return;\n    }\n\n    const announcer = createRouteAnnouncer(router, announceOptions);\n\n    return () => {\n      announcer.destroy();\n    };\n    // announceOptions (for getAnnouncementText) omitted — inline-object identity\n    // churn shouldn't re-create the announcer; the callback is captured once by\n    // the utility (same rationale as scrollContainer below).\n    // eslint-disable-next-line @eslint-react/exhaustive-deps\n  }, [router, announceEnabled, announcePrefix]);\n\n  // Primitive deps so inline `{ mode: \"restore\" }` doesn't thrash on every\n  // render. scrollContainer is a getter invoked lazily on every event inside\n  // the utility — swapping its reference doesn't change the resolved element,\n  // so we intentionally omit it from deps to keep inline getters stable.\n  const srMode = scrollRestoration?.mode;\n  const srAnchor = scrollRestoration?.anchorScrolling;\n  const srBehavior = scrollRestoration?.behavior;\n  const srStorageKey = scrollRestoration?.storageKey;\n  const srEnabled = scrollRestoration !== undefined;\n\n  useEffect(() => {\n    if (!srEnabled) {\n      return;\n    }\n\n    const sr = createScrollRestoration(router, {\n      mode: srMode,\n      anchorScrolling: srAnchor,\n      behavior: srBehavior,\n      storageKey: srStorageKey,\n      // srEnabled check above guarantees scrollRestoration is defined.\n      scrollContainer: scrollRestoration.scrollContainer,\n    });\n\n    return () => {\n      sr.destroy();\n    };\n    // scrollRestoration (for scrollContainer) omitted — see comment above.\n    // eslint-disable-next-line @eslint-react/exhaustive-deps\n  }, [router, srEnabled, srMode, srAnchor, srBehavior, srStorageKey]);\n\n  const spySelector = scrollSpy?.selector;\n  const spyRootMargin = scrollSpy?.rootMargin;\n  const spyEnabled =\n    scrollSpy !== undefined && spySelector !== undefined && spySelector !== \"\";\n\n  useEffect(() => {\n    if (!spyEnabled) {\n      return;\n    }\n\n    const spy = createScrollSpy(router, {\n      selector: spySelector,\n      rootMargin: spyRootMargin,\n      scrollContainer: scrollSpy.scrollContainer,\n    });\n\n    return () => {\n      spy.destroy();\n    };\n    // scrollSpy (for scrollContainer) omitted — same rationale as\n    // scrollRestoration above: getter is invoked lazily inside the utility,\n    // identity changes don't affect resolution.\n    // eslint-disable-next-line @eslint-react/exhaustive-deps\n  }, [router, spyEnabled, spySelector, spyRootMargin]);\n\n  useEffect(() => {\n    if (!viewTransitions) {\n      return;\n    }\n\n    const vt = createViewTransitions(router);\n\n    return () => {\n      vt.destroy();\n    };\n  }, [router, viewTransitions]);\n\n  return <RouterProviderCore router={router}>{children}</RouterProviderCore>;\n};\n"],"mappings":"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"}