{"version":3,"file":"client.cjs","names":[],"sources":["../../../src/router/client.ts"],"sourcesContent":["/**\n * Client-side router for Nix.js Kit.\n *\n * Intercepts clicks on internal links, fetches the rendered page body from\n * `/__nix-js/render`, swaps the `#app` content and updates the history state.\n * This is loaded as part of the client bundle instead of being inlined in\n * every HTML page.\n *\n * Features:\n * - SPA navigation with head merge (title, meta, OG tags)\n * - Scroll restoration on back/forward\n * - Prefetch on hover/focus (Astro-style), with opt-in viewport prefetch\n * - View Transitions API with `prefers-reduced-motion` respect\n */\n\ninterface RenderPayload {\n  title?: string;\n  body: string;\n  /** Set-Cookie value relayed by the server to clear a consumed action error. */\n  clearActionErrorCookie?: string;\n  /** `<head>` tags (title, meta, OG, twitter) to merge on navigation. */\n  head?: string;\n}\n\n/**\n * Whether the `/__nix-js/render` endpoint has been detected. Static builds emit\n * `<meta name=\"nix-js:render-endpoint\" content=\"off\">` so this starts as\n * `false` with zero probe requests. For older builds, a single shared probe\n * determines availability so concurrent prefetches never storm the endpoint.\n */\nlet renderEndpointAvailable = true;\n\n/** Shared in-flight probe promise; at most one request hits the endpoint. */\nlet endpointProbe: Promise<boolean> | null = null;\n\n/** Resolves endpoint availability, caching the result for the page lifetime. */\nfunction resolveEndpointAvailability(): Promise<boolean> {\n  if (!renderEndpointAvailable) return Promise.resolve(false);\n  if (!endpointProbe) {\n    endpointProbe = (async () => {\n      const url = new URL(\"/__nix-js/render\", location.origin);\n      url.searchParams.set(\"page\", \"/\");\n      try {\n        const response = await fetch(url.toString(), {\n          headers: { Accept: \"application/json\", \"X-Nix-Probe\": \"1\" },\n        });\n        renderEndpointAvailable = response.ok;\n        return response.ok;\n      } catch {\n        renderEndpointAvailable = false;\n        return false;\n      }\n    })();\n  }\n  return endpointProbe;\n}\n\nfunction isInternalLink(link: HTMLAnchorElement): boolean {\n  return (\n    link.tagName === \"A\" &&\n    link.hostname === location.hostname &&\n    link.target === \"\" &&\n    !link.getAttribute(\"download\") &&\n    !link.hasAttribute(\"data-no-router\")\n  );\n}\n\nfunction hasModifier(event: MouseEvent): boolean {\n  return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;\n}\n\n// --- Prefetch cache ---\n\nconst PREFETCH_TTL_MS = 30_000; // 30 seconds\n\ninterface CacheEntry {\n  payload: RenderPayload;\n  ts: number;\n}\n\nconst prefetchCache = new Map<string, CacheEntry>();\n\n/** Builds the cache key from pathname + search. */\nfunction cacheKey(pathname: string, search: string): string {\n  return pathname + search;\n}\n\n/** Returns a cached payload if fresh, otherwise undefined. */\nfunction getCached(key: string): RenderPayload | undefined {\n  const entry = prefetchCache.get(key);\n  if (!entry) return undefined;\n  if (Date.now() - entry.ts > PREFETCH_TTL_MS) {\n    prefetchCache.delete(key);\n    return undefined;\n  }\n  return entry.payload;\n}\n\n/** Stores a payload in the prefetch cache. */\nfunction setCached(key: string, payload: RenderPayload): void {\n  prefetchCache.set(key, { payload, ts: Date.now() });\n}\n\n/**\n * Fetches the render payload for a path. Uses the prefetch cache when fresh.\n * Stores the result in the cache for subsequent navigations.\n *\n * On static deployments (no `/__nix-js/render` endpoint), falls back to\n * fetching the full HTML page and extracting `#app` + `<head>` tags.\n */\nasync function fetchPayload(pathname: string, search: string, signal?: AbortSignal): Promise<RenderPayload | undefined> {\n  const key = cacheKey(pathname, search);\n  const cached = getCached(key);\n  if (cached) return cached;\n\n  // Wait on the shared probe so concurrent prefetches generate at most ONE\n  // request against the endpoint (the rest go straight to the HTML fallback).\n  if (renderEndpointAvailable) {\n    if (await resolveEndpointAvailability()) {\n      const payload = await fetchFromRenderEndpoint(pathname, search, signal);\n      if (payload) {\n        setCached(key, payload);\n        return payload;\n      }\n      // The endpoint exists but couldn't render this specific page — fall\n      // through to the HTML-based fetch without disabling it globally.\n    }\n  }\n\n  // Static fallback: fetch the full HTML page and extract #app + head.\n  const payload = await fetchFromHtml(pathname, search, signal);\n  if (payload) {\n    setCached(key, payload);\n  }\n  return payload;\n}\n\n/** Attempts to fetch from the `/__nix-js/render` JSON endpoint. */\nasync function fetchFromRenderEndpoint(pathname: string, search: string, signal?: AbortSignal): Promise<RenderPayload | undefined> {\n  const url = new URL(\"/__nix-js/render\", location.origin);\n  url.searchParams.set(\"page\", pathname);\n  const current = new URL(location.href);\n  url.searchParams.set(\"search\", search || current.search);\n\n  let response: Response;\n  try {\n    response = await fetch(url.toString(), { headers: { Accept: \"application/json\" }, signal });\n  } catch (err) {\n    if (err instanceof DOMException && err.name === \"AbortError\") return undefined;\n    return undefined;\n  }\n  if (!response.ok) return undefined;\n\n  let payload: RenderPayload;\n  try {\n    payload = await response.json();\n  } catch {\n    return undefined;\n  }\n  return payload;\n}\n\n/**\n * Static-mode fallback: fetches the full HTML page for the path and extracts\n * the `#app` innerHTML plus managed `<head>` tags (`[data-nix-js-head]`).\n * Also extracts `<title>`, stylesheets, and headLinks for SPA navigation.\n */\nasync function fetchFromHtml(pathname: string, search: string, signal?: AbortSignal): Promise<RenderPayload | undefined> {\n  const fullUrl = pathname + (search || \"\");\n  let response: Response;\n  try {\n    response = await fetch(fullUrl, { headers: { Accept: \"text/html\" }, signal });\n  } catch (err) {\n    if (err instanceof DOMException && err.name === \"AbortError\") return undefined;\n    return undefined;\n  }\n  if (!response.ok) return undefined;\n\n  let html: string;\n  try {\n    html = await response.text();\n  } catch {\n    return undefined;\n  }\n\n  // Parse the full HTML document.\n  const parser = new DOMParser();\n  const doc = parser.parseFromString(html, \"text/html\");\n\n  // Extract #app innerHTML — this is the page body.\n  const appEl = doc.getElementById(\"app\");\n  if (!appEl) return undefined;\n  const body = appEl.innerHTML;\n\n  // Extract managed head tags (data-nix-js-head) for mergeHead.\n  const headTags = doc.querySelectorAll(\"[data-nix-js-head]\");\n  let head = \"\";\n  for (const tag of headTags) {\n    head += tag.outerHTML;\n  }\n\n  // Also extract headLinks (favicons, manifest, theme-color) so they persist.\n  // These don't have data-nix-js-head, so we grab them separately.\n  const linkTags = doc.head.querySelectorAll(\"link[rel='icon'], link[rel='apple-touch-icon'], link[rel='manifest'], meta[name='theme-color']\");\n  for (const tag of linkTags) {\n    // Skip if already in the current document head\n    const href = tag.getAttribute(\"href\");\n    if (href && document.head.querySelector(`link[href=\"${href}\"]`)) continue;\n    head += tag.outerHTML;\n  }\n\n  const title = doc.querySelector(\"title\")?.textContent ?? undefined;\n\n  return { body, head, title };\n}\n\n/**\n * Prefetches a path without navigating. Called by the IntersectionObserver\n * when a link enters the viewport, and on hover/focus.\n */\nexport async function prefetch(pathname: string, search = \"\"): Promise<void> {\n  const key = cacheKey(pathname, search);\n  if (prefetchCache.has(key)) {\n    // Already cached or in-flight — skip.\n    const entry = prefetchCache.get(key)!;\n    if (Date.now() - entry.ts <= PREFETCH_TTL_MS) return;\n  }\n  await fetchPayload(pathname, search);\n}\n\n// --- View Transitions ---\n\n/** Returns true if the user prefers reduced motion. */\nfunction prefersReducedMotion(): boolean {\n  return window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches ?? false;\n}\n\n/** Returns true if the View Transitions API is available. */\nfunction supportsViewTransitions(): boolean {\n  return typeof (document as any).startViewTransition === \"function\";\n}\n\n// --- Navigation ---\n\n/**\n * Guard against concurrent navigations. When a navigation is in-flight, a new\n * request cancels the previous one (abort + ignore its result). This prevents\n * race conditions where two rapid clicks could swap content out of order.\n */\nlet inFlightNavigation: {\n  controller: AbortController;\n  pathname: string;\n} | null = null;\n\n/**\n * Cancels any in-flight navigation so a new one can proceed cleanly.\n */\nfunction cancelInFlightNavigation(): void {\n  if (inFlightNavigation) {\n    inFlightNavigation.controller.abort();\n    inFlightNavigation = null;\n  }\n}\n\n/**\n * Hoists `<link rel=\"stylesheet\">` and `<style>` tags from inside `#app` into\n * `<head>` so they persist across SPA navigations (prevents FOUC/flashing).\n * Deduplicates by `href` for links and by text content for styles.\n */\nexport function hoistStyles(container: ParentNode): void {\n  const links = container.querySelectorAll<HTMLLinkElement>('link[rel=\"stylesheet\"]');\n  for (const link of links) {\n    const href = link.getAttribute(\"href\");\n    if (!href) continue;\n    // Already in <head>?\n    const existing = document.head.querySelector(`link[rel=\"stylesheet\"][href=\"${href}\"]`);\n    if (existing) {\n      link.remove();\n      continue;\n    }\n    // Mark as hoisted so we can clean up later if needed\n    link.setAttribute(\"data-nix-js-hoisted\", \"\");\n    document.head.appendChild(link);\n  }\n\n  const styles = container.querySelectorAll<HTMLStyleElement>(\"style\");\n  for (const style of styles) {\n    const text = style.textContent?.trim();\n    if (!text) continue;\n    // Check if an identical style already exists in <head>\n    const existing = Array.from(document.head.querySelectorAll(\"style\")).find(\n      (s) => s.textContent?.trim() === text,\n    );\n    if (existing) {\n      style.remove();\n      continue;\n    }\n    style.setAttribute(\"data-nix-js-hoisted\", \"\");\n    document.head.appendChild(style);\n  }\n}\n\n/**\n * Announces a route change to assistive technology via an aria-live region.\n * This is critical for screen reader users who need to know the page content\n * has changed after a SPA navigation.\n */\nfunction announceNavigation(pathname: string): void {\n  let liveRegion = document.getElementById(\"nix-js-route-announcer\");\n  if (!liveRegion) {\n    liveRegion = document.createElement(\"div\");\n    liveRegion.id = \"nix-js-route-announcer\";\n    liveRegion.setAttribute(\"aria-live\", \"assertive\");\n    liveRegion.setAttribute(\"aria-atomic\", \"true\");\n    liveRegion.setAttribute(\"role\", \"status\");\n    // Visually hidden but available to screen readers.\n    liveRegion.setAttribute(\"style\", \"position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;\");\n    document.body.appendChild(liveRegion);\n  }\n  // Clear and re-set so screen readers announce the change.\n  liveRegion.textContent = \"\";\n  // Use a microtask delay so the DOM update is picked up by AT.\n  // Guard against the document being torn down (e.g. in tests).\n  const region = liveRegion;\n  const timer = setTimeout(() => {\n    if (typeof document !== \"undefined\" && region) {\n      const title = document.title || pathname;\n      region.textContent = title;\n    }\n  }, 50);\n  // Don't keep the process alive just for the announcer.\n  if (typeof timer === \"object\" && timer && \"unref\" in timer) {\n    (timer as { unref: () => void }).unref();\n  }\n}\n\n/**\n * Moves focus to the main content area after a SPA navigation. This follows\n * the WAI-ARIA pattern for route changes: if the #app has a tabindex=-1, focus\n * it; otherwise create a temporary focus target.\n */\nfunction moveFocusToContent(): void {\n  const app = document.getElementById(\"app\");\n  if (!app) return;\n  // Ensure the container is focusable.\n  if (!app.hasAttribute(\"tabindex\")) {\n    app.setAttribute(\"tabindex\", \"-1\");\n  }\n  // Remove outline only for mouse users; keyboard users keep it.\n  app.focus({ preventScroll: false });\n}\n\n/**\n * Updates the canonical URL and og:url meta tags after navigation.\n */\nfunction updateCanonicalUrl(pathname: string, search: string): void {\n  const fullUrl = location.origin + pathname + (search || \"\");\n  // Update or create canonical link.\n  let canonical = document.querySelector<HTMLLinkElement>('link[rel=\"canonical\"]');\n  if (!canonical) {\n    canonical = document.createElement(\"link\");\n    canonical.rel = \"canonical\";\n    document.head.appendChild(canonical);\n  }\n  canonical.href = fullUrl;\n  // Update og:url meta.\n  let ogUrl = document.querySelector<HTMLMetaElement>('meta[property=\"og:url\"]');\n  if (!ogUrl) {\n    ogUrl = document.createElement(\"meta\");\n    ogUrl.setAttribute(\"property\", \"og:url\");\n    document.head.appendChild(ogUrl);\n  }\n  ogUrl.content = fullUrl;\n}\n\n/**\n * Navigates to a page without a full reload: fetches the fresh body from the\n * `/__nix-js/render` endpoint, swaps `#app`, updates the document title and\n * dispatches `nix-js:rendered` so islands re-hydrate. Used by the router on\n * clicks and available for programmatic navigation (e.g. after a server\n * action returns a redirect, so the target page shows fresh server data).\n *\n * Concurrent navigations are handled: a new navigateTo() cancels any\n * in-flight navigation to prevent out-of-order content swaps.\n *\n * @param pathname Path without query, e.g. \"/movies/inception\".\n * @param search Query string, e.g. \"?reviewed=1\" (optional).\n * @param push Whether to push a history entry (default true).\n * @returns true on success, false if the render failed.\n */\nexport async function navigateTo(pathname: string, search = \"\", push = true): Promise<boolean> {\n  // Cancel any previous in-flight navigation to prevent race conditions.\n  cancelInFlightNavigation();\n\n  const controller = new AbortController();\n  inFlightNavigation = { controller, pathname };\n\n  let payload: RenderPayload | undefined;\n  try {\n    payload = await fetchPayload(pathname, search, controller.signal);\n  } catch (err) {\n    if (controller.signal.aborted) return false; // superseded by a newer nav\n    throw err;\n  }\n\n  // If a newer navigation started while we were fetching, bail out.\n  if (inFlightNavigation && inFlightNavigation.controller !== controller) {\n    if (controller.signal.aborted) return false;\n  }\n  inFlightNavigation = null;\n\n  if (!payload) return false;\n\n  const app = document.getElementById(\"app\");\n  if (!app) return false;\n\n  // Save scroll position in the current history entry before navigating.\n  if (push) {\n    history.replaceState(\n      { n: location.pathname, scroll: window.scrollY },\n      \"\",\n      location.href,\n    );\n  }\n\n  const current = new URL(location.href);\n  const doSwap = () => {\n    // Save scroll positions of scrollable elements (e.g. sidebar) before swap\n    const scrollables: { el: Element; top: number }[] = [];\n    app.querySelectorAll(\"[data-scroll-preserve]\").forEach((el) => {\n      scrollables.push({ el, top: el.scrollTop });\n    });\n\n    // Hoist any stylesheets from the current #app content to <head> before\n    // the swap, so they persist and don't cause a flash.\n    hoistStyles(app);\n\n    // Parse the new body and hoist its styles before injecting, so the\n    // browser never sees a frame without styles.\n    const temp = document.createElement(\"template\");\n    temp.innerHTML = payload.body;\n    hoistStyles(temp.content as unknown as HTMLElement);\n\n    // Inject the remaining body (styles already moved to <head>)\n    app.innerHTML = temp.innerHTML;\n    mergeHead(payload.head, payload.title);\n    if (payload.clearActionErrorCookie) {\n      document.cookie = payload.clearActionErrorCookie;\n    }\n    if (push) {\n      history.pushState({ n: pathname, scroll: 0 }, \"\", pathname + (search || current.search));\n    }\n    const savedScroll = push ? 0 : (history.state?.scroll ?? 0);\n    window.scrollTo(0, savedScroll);\n\n    // Restore scroll positions of preserved elements\n    for (const s of scrollables) {\n      const newEl = app.querySelector(`[data-scroll-preserve=\"${s.el.getAttribute(\"data-scroll-preserve\")}\"]`);\n      if (newEl) newEl.scrollTop = s.top;\n    }\n\n    // Update canonical URL and OG tags for the new route.\n    updateCanonicalUrl(pathname, search);\n\n    // Announce the navigation to screen readers.\n    announceNavigation(pathname);\n\n    // Move focus to the main content for keyboard/screen reader users.\n    // Only on push (forward navigation), not on back/forward (popstate).\n    if (push) moveFocusToContent();\n\n    document.dispatchEvent(new CustomEvent(\"nix-js:rendered\"));\n  };\n\n  // Use View Transitions when available and the user hasn't opted out.\n  const useTransition = supportsViewTransitions() && !prefersReducedMotion();\n  if (useTransition) {\n    (document as any).startViewTransition(() => doSwap());\n  } else {\n    doSwap();\n  }\n\n  return true;\n}\n\n/**\n * Replaces all `<head>` tags marked with `data-nix-js-head` with the new ones\n * from the server payload. Also updates `document.title` when a title tag is\n * present in the new head.\n */\nfunction mergeHead(head: string | undefined, fallbackTitle: string | undefined): void {\n  // Remove existing managed tags.\n  const existing = document.querySelectorAll(\"[data-nix-js-head]\");\n  existing.forEach((el) => el.remove());\n\n  if (head && head.trim().length > 0) {\n    // Parse the head tags from the server and insert them into <head>.\n    const parser = document.createElement(\"template\");\n    parser.innerHTML = head;\n    const fragment = parser.content;\n    // Extract the <title> if present and set document.title directly.\n    const titleEl = fragment.querySelector(\"title\");\n    if (titleEl) {\n      document.title = titleEl.textContent ?? \"\";\n      titleEl.remove();\n    }\n    document.head.appendChild(fragment);\n  } else if (fallbackTitle) {\n    document.title = fallbackTitle;\n  }\n}\n\n// --- Link prefetch observers ---\n\n/** Set of links currently being observed for prefetch. */\nconst observedLinks = new WeakSet<HTMLAnchorElement>();\n\n/**\n * Sets up prefetch on internal links. Default is interaction-only (hover or\n * focus) — the same behavior as Astro — so a page load never fires a burst of\n * fetches for every link in the viewport. Links can opt into viewport\n * prefetching with `data-prefetch=\"viewport\"`.\n */\nfunction setupLinkPrefetch(): void {\n  const linkInfo = (link: HTMLAnchorElement) => {\n    const href = link.getAttribute(\"href\");\n    if (!href || href.startsWith(\"#\") || href.startsWith(\"mailto:\") || href.startsWith(\"javascript:\")) {\n      return null;\n    }\n    const qIndex = href.indexOf(\"?\");\n    return {\n      path: qIndex === -1 ? href : href.slice(0, qIndex),\n      search: qIndex === -1 ? \"\" : href.slice(qIndex),\n    };\n  };\n\n  const observeLink = (link: HTMLAnchorElement) => {\n    if (observedLinks.has(link)) return;\n    if (!isInternalLink(link) || link.hasAttribute(\"data-no-prefetch\")) return;\n    observedLinks.add(link);\n\n    // Interaction prefetch (default): hover or focus.\n    const onInteract = () => {\n      const info = linkInfo(link);\n      if (info) void prefetch(info.path, info.search);\n    };\n    link.addEventListener(\"pointerenter\", onInteract, { once: true });\n    link.addEventListener(\"focus\", onInteract, { once: true });\n\n    // Opt-in viewport prefetch via data-prefetch=\"viewport\".\n    if (link.dataset.prefetch === \"viewport\" && \"IntersectionObserver\" in window) {\n      const observer = new IntersectionObserver(\n        (entries) => {\n          for (const entry of entries) {\n            if (!entry.isIntersecting) continue;\n            const info = linkInfo(entry.target as HTMLAnchorElement);\n            if (info) void prefetch(info.path, info.search);\n            observer.disconnect();\n          }\n        },\n        { rootMargin: \"200px\", threshold: 0 },\n      );\n      observer.observe(link);\n    }\n  };\n\n  const observeLinks = () => {\n    const links = document.querySelectorAll<HTMLAnchorElement>(\"a[href]\");\n    for (const link of links) observeLink(link);\n  };\n\n  observeLinks();\n\n  // Re-scan when the DOM changes (e.g. after SPA navigation).\n  const mutationObserver = new MutationObserver(() => observeLinks());\n  mutationObserver.observe(document.body, { childList: true, subtree: true });\n\n  // Re-scan after each SPA navigation.\n  document.addEventListener(\"nix-js:rendered\", observeLinks);\n}\n\n// --- Router bootstrap ---\n\nexport function startClientRouter(): void {\n  // Static builds emit this marker, so the client never probes the render\n  // endpoint (zero 404s on fully static deployments). The meta lives in the\n  // initial HTML head and persists across SPA navigations.\n  const endpointMeta = document.querySelector<HTMLMetaElement>(\n    'meta[name=\"nix-js:render-endpoint\"]',\n  );\n  if (endpointMeta?.getAttribute(\"content\") === \"off\") {\n    renderEndpointAvailable = false;\n  }\n\n  // Hoist styles from #app to <head> immediately on page load.\n  // This prevents FOUC on the first SPA navigation.\n  const app = document.getElementById(\"app\");\n  if (app) hoistStyles(app);\n\n  document.addEventListener(\"click\", async (event) => {\n    if (!(event instanceof MouseEvent) || hasModifier(event)) return;\n    if (event.defaultPrevented) return;\n    const link = (event.target as HTMLElement).closest(\"a\");\n    if (!link || !isInternalLink(link as HTMLAnchorElement)) return;\n\n    const href = link.getAttribute(\"href\");\n    if (!href || href.startsWith(\"mailto:\") || href.startsWith(\"javascript:\")) return;\n\n    // Handle hash links: scroll to the element if it exists on the page\n    if (href.startsWith(\"#\")) {\n      if (href.length > 1) {\n        const target = document.getElementById(href.slice(1));\n        if (target) {\n          event.preventDefault();\n          target.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n          history.replaceState(null, \"\", href);\n        }\n      }\n      return;\n    }\n\n    event.preventDefault();\n    const qIndex = href.indexOf(\"?\");\n    const path = qIndex === -1 ? href : href.slice(0, qIndex);\n    const search = qIndex === -1 ? \"\" : href.slice(qIndex);\n    if (!(await navigateTo(path, search))) {\n      location.assign(href);\n    }\n  });\n\n  window.addEventListener(\"popstate\", (event) => {\n    const state = event.state as { n?: string; scroll?: number } | null;\n    const target = state?.n ?? location.pathname;\n    void navigateTo(target, location.search, false);\n  });\n\n  setupLinkPrefetch();\n}\n\n// --- Test helpers (not part of the public API) ---\n\n/**\n * Resets all internal router state. Intended for test isolation only.\n * @internal\n */\nexport function __resetRouterState(): void {\n  prefetchCache.clear();\n  inFlightNavigation = null;\n  renderEndpointAvailable = true;\n  endpointProbe = null;\n}\n\n"],"mappings":"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"}