@ilha/router 0.8.5 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -65,6 +65,10 @@ export type Loader<T> = (ctx: LoaderContext) => Promise<T> | T;
65
65
  /**
66
66
  * Identity function for declaring a loader. Exists purely as a type anchor and
67
67
  * a marker for the Vite plugin to detect by export name.
68
+ *
69
+ * Loaders must read `ctx.params`/`ctx.url` rather than `useRoute()` — the
70
+ * route store still holds the previous route while a navigation's loader is
71
+ * in flight, so `useRoute().params()` inside a loader reads stale params.
68
72
  */
69
73
  export declare function loader<T>(fn: Loader<T>): Loader<T>;
70
74
  /** Extract the return type of a loader. */
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { A as wrapLayout, C as routePath, D as src_default, E as serializeHead, M as setHistoryMode, O as useRoute, S as routeParams, T as router, _ as navigating, a as RouterView, b as redirect, c as composeLoaders, d as error, f as head, g as navigate, h as loader, i as RouterLink, j as getHistoryMode, k as wrapError, l as defineLayout, m as isActive, n as LoaderError, o as afterNavigate, p as invalidate, r as Redirect, s as beforeNavigate, t as LOADER_ENDPOINT, u as enableLinkInterception, v as prefetch, w as routeSearch, x as routeHash, y as prime } from "./src-C2_zEYnV.js";
1
+ import { A as wrapLayout, C as routePath, D as src_default, E as serializeHead, M as setHistoryMode, O as useRoute, S as routeParams, T as router, _ as navigating, a as RouterView, b as redirect, c as composeLoaders, d as error, f as head, g as navigate, h as loader, i as RouterLink, j as getHistoryMode, k as wrapError, l as defineLayout, m as isActive, n as LoaderError, o as afterNavigate, p as invalidate, r as Redirect, s as beforeNavigate, t as LOADER_ENDPOINT, u as enableLinkInterception, v as prefetch, w as routeSearch, x as routeHash, y as prime } from "./src-Dfq3JPRz.js";
2
2
 
3
3
  export { LOADER_ENDPOINT, LoaderError, Redirect, RouterLink, RouterView, afterNavigate, beforeNavigate, composeLoaders, src_default as default, defineLayout, enableLinkInterception, error, getHistoryMode, head, invalidate, isActive, loader, navigate, navigating, prefetch, prime, redirect, routeHash, routeParams, routePath, routeSearch, router, serializeHead, setHistoryMode, useRoute, wrapError, wrapLayout };
@@ -1,4 +1,4 @@
1
- import ilha, { ISLAND_MOUNT_INTERNAL, context, html, mount } from "ilha";
1
+ import ilha, { ISLAND_MOUNT_HANDLES, ISLAND_MOUNT_INTERNAL, context, html, mount } from "ilha";
2
2
  import { addRoute, createRouter, findRoute } from "rou3";
3
3
 
4
4
  //#region src/hash.ts
@@ -136,6 +136,10 @@ const isBrowser = typeof window !== "undefined" && typeof document !== "undefine
136
136
  /**
137
137
  * Identity function for declaring a loader. Exists purely as a type anchor and
138
138
  * a marker for the Vite plugin to detect by export name.
139
+ *
140
+ * Loaders must read `ctx.params`/`ctx.url` rather than `useRoute()` — the
141
+ * route store still holds the previous route while a navigation's loader is
142
+ * in flight, so `useRoute().params()` inside a loader reads stale params.
139
143
  */
140
144
  function loader(fn) {
141
145
  return fn;
@@ -306,7 +310,6 @@ function injectKPageSlot(layoutHtml, slotInnerHtml, which) {
306
310
  const target = which === "innermost" ? spans[spans.length - 1] : spans[0];
307
311
  return layoutHtml.slice(0, target.openEnd) + slotInnerHtml + layoutHtml.slice(target.closeStart);
308
312
  }
309
- /** Layout shell HTML with an empty `k:page` — avoids scanning MDX/twoslash inside `Wrapped.toString()`. */
310
313
  function layoutHtmlWithEmptyKPage(wrappedLayout, props) {
311
314
  const handler = wrappedLayout[WRAP_LAYOUT_HANDLER];
312
315
  if (!handler) return wrappedLayout.toString(props);
@@ -393,6 +396,43 @@ function wrapLayout(layout, page) {
393
396
  };
394
397
  }
395
398
  wrapLeafPageMountHooks(leafPage);
399
+ const pageHandles = /* @__PURE__ */ new Map();
400
+ const pageInternalBase = page[ISLAND_MOUNT_INTERNAL];
401
+ if (typeof pageInternalBase === "function") page[ISLAND_MOUNT_INTERNAL] = (host, props) => {
402
+ const handle = pageInternalBase(host, props);
403
+ const entry = {
404
+ handle,
405
+ mountProps: props
406
+ };
407
+ pageHandles.set(host, entry);
408
+ return {
409
+ unmount: () => {
410
+ if (pageHandles.get(host) === entry) pageHandles.delete(host);
411
+ return handle.unmount();
412
+ },
413
+ updateProps: (p) => {
414
+ entry.mountProps = p;
415
+ handle.updateProps(p);
416
+ }
417
+ };
418
+ };
419
+ /**
420
+ * In-place prop update for one mounted layout instance: refresh the
421
+ * merged-input ref (so any layout re-render passes fresh props to `k:page`),
422
+ * push the merged loader props directly into the page mounted under *this*
423
+ * layout host (spread over its latest slot props so layout-provided extras
424
+ * survive), then update the layout island's own input. Page-first ordering
425
+ * lets a layout that *does* read its input re-render afterwards and win with
426
+ * its own fresher slot props.
427
+ */
428
+ const layoutUpdateProps = (layoutHost, coreUpdate) => (p) => {
429
+ setLayoutMergedInput(p);
430
+ for (const [pageHost, entry] of pageHandles) if (layoutHost.contains(pageHost)) entry.handle.updateProps({
431
+ ...entry.mountProps ?? {},
432
+ ...p ?? {}
433
+ });
434
+ coreUpdate?.(p);
435
+ };
396
436
  const layoutMount = Wrapped.mount.bind(Wrapped);
397
437
  const layoutInternal = Wrapped[ISLAND_MOUNT_INTERNAL];
398
438
  function prepareLayoutMountHost(host) {
@@ -401,16 +441,27 @@ function wrapLayout(layout, page) {
401
441
  Wrapped.mount = (host, props) => {
402
442
  setLayoutMergedInput(props);
403
443
  prepareLayoutMountHost(host);
404
- return layoutMount(host, props);
444
+ const unmount = layoutMount(host, props);
445
+ const core = ISLAND_MOUNT_HANDLES.get(host);
446
+ ISLAND_MOUNT_HANDLES.set(host, {
447
+ unmount,
448
+ updateProps: layoutUpdateProps(host, core?.updateProps)
449
+ });
450
+ return unmount;
405
451
  };
406
452
  Wrapped[ISLAND_MOUNT_INTERNAL] = (host, props) => {
407
453
  setLayoutMergedInput(props);
408
454
  prepareLayoutMountHost(host);
409
- if (typeof layoutInternal === "function") return layoutInternal(host, props);
410
- return {
455
+ const base = typeof layoutInternal === "function" ? layoutInternal(host, props) : {
411
456
  unmount: layoutMount(host, props),
412
457
  updateProps: () => {}
413
458
  };
459
+ const enhanced = {
460
+ unmount: base.unmount,
461
+ updateProps: layoutUpdateProps(host, base.updateProps)
462
+ };
463
+ ISLAND_MOUNT_HANDLES.set(host, enhanced);
464
+ return enhanced;
414
465
  };
415
466
  Wrapped.hydratable = async (props, opts) => {
416
467
  if (!opts?.name) throw new Error("wrapLayout: hydratable requires options.name");
@@ -640,6 +691,51 @@ function prefetch(pathWithSearch) {
640
691
  expires: Date.now() + PREFETCH_TTL_MS
641
692
  });
642
693
  }
694
+ const noopRouteHandle = () => ({
695
+ unmount: () => {},
696
+ updateProps: null
697
+ });
698
+ /** Mount an island keeping the full internal handle so later same-island
699
+ * navigations can push new loader props instead of remounting. */
700
+ function mountIslandWithHandle(island, host, props) {
701
+ const internal = island[ISLAND_MOUNT_INTERNAL];
702
+ if (typeof internal === "function") {
703
+ const h = internal(host, props);
704
+ return {
705
+ unmount: () => void h.unmount(),
706
+ updateProps: (p) => h.updateProps(p)
707
+ };
708
+ }
709
+ return {
710
+ unmount: island.mount(host, props),
711
+ updateProps: null
712
+ };
713
+ }
714
+ /**
715
+ * Same-island fast path: fetch fresh loader data and push it into the mounted
716
+ * island via `updateProps` — ilha's fine-grained morph reconciles the DOM, so
717
+ * focus, caret, selection, and scroll survive (the reason persistQuery-driven
718
+ * filter inputs don't blur while typing). Returns "updated" when applied (or
719
+ * redirected), "remount" when the caller must run the full teardown + mount
720
+ * path (loader error / not-found need boundary DOM; the full path re-fetches,
721
+ * accepted for these rare cases). Throws AbortError when superseded.
722
+ */
723
+ async function updateRouteInPlace(handle, pathWithSearch, signal) {
724
+ if (!handle.updateProps) return "remount";
725
+ const result = !!findRoute(_rou3, "GET", pathWithSearch.split("?")[0] ?? "")?.data?.hasLoader ? await fetchLoaderData(pathWithSearch, signal) : {
726
+ kind: "data",
727
+ data: {}
728
+ };
729
+ signal.throwIfAborted();
730
+ if (result.kind === "redirect") {
731
+ clientRedirect(result.to);
732
+ return "updated";
733
+ }
734
+ if (result.kind !== "data") return "remount";
735
+ if (result.headEntries?.length) applyHeadEntriesToDocument([...result.headEntries]);
736
+ handle.updateProps(result.data);
737
+ return "updated";
738
+ }
643
739
  /**
644
740
  * Mounts a route island with proper hydration for client-side navigation.
645
741
  * Looks up the island in the reverse registry, runs the loader (via fetch),
@@ -652,13 +748,16 @@ async function mountRouteWithHydration(island, host, pathWithSearch, signal, reg
652
748
  return withViewSwap(() => {
653
749
  host.innerHTML = `<div data-router-view data-router-not-found>${nf.toString()}</div>`;
654
750
  const nfHost = host.firstElementChild;
655
- return nfHost ? nf.mount(nfHost) : () => {};
751
+ return {
752
+ unmount: nfHost ? nf.mount(nfHost) : () => {},
753
+ updateProps: null
754
+ };
656
755
  });
657
756
  }
658
757
  await withViewSwap(() => {
659
758
  host.innerHTML = `<div data-router-empty></div>`;
660
759
  });
661
- return () => {};
760
+ return noopRouteHandle();
662
761
  }
663
762
  const clientMatch = findRoute(_rou3, "GET", pathWithSearch.split("?")[0] ?? "");
664
763
  const hasLoader = !!clientMatch?.data?.hasLoader;
@@ -669,22 +768,25 @@ async function mountRouteWithHydration(island, host, pathWithSearch, signal, reg
669
768
  };
670
769
  if (loaderResult.kind === "redirect") {
671
770
  clientRedirect(loaderResult.to);
672
- return () => {};
771
+ return noopRouteHandle();
673
772
  }
674
773
  if (loaderResult.kind === "error") {
675
774
  const boundary = clientMatch?.data?.errorHandler;
676
- if (boundary) return withViewSwap(() => mountLoaderErrorBoundary(boundary, host, loaderResult.status, loaderResult.message));
775
+ if (boundary) return withViewSwap(() => ({
776
+ unmount: mountLoaderErrorBoundary(boundary, host, loaderResult.status, loaderResult.message),
777
+ updateProps: null
778
+ }));
677
779
  const escaped = escapeHtml(loaderResult.message);
678
780
  await withViewSwap(() => {
679
781
  host.innerHTML = `<div data-router-view data-router-error="${loaderResult.status}">${escaped}</div>`;
680
782
  });
681
- return () => {};
783
+ return noopRouteHandle();
682
784
  }
683
785
  if (loaderResult.kind === "not-found") {
684
786
  await withViewSwap(() => {
685
787
  host.innerHTML = `<div data-router-empty></div>`;
686
788
  });
687
- return () => {};
789
+ return noopRouteHandle();
688
790
  }
689
791
  props = loaderResult.data;
690
792
  const headStore = { entries: [...loaderResult.headEntries ?? []] };
@@ -695,7 +797,7 @@ async function mountRouteWithHydration(island, host, pathWithSearch, signal, reg
695
797
  applyHeadEntriesToDocument(headStore.entries);
696
798
  host.innerHTML = `<div data-router-view>${html}</div>`;
697
799
  });
698
- return () => {};
800
+ return noopRouteHandle();
699
801
  }
700
802
  const name = reverseRegistry?.get(island) ?? Object.entries(registry).find(([, v]) => v === island)?.[0];
701
803
  if (!name) {
@@ -705,7 +807,7 @@ async function mountRouteWithHydration(island, host, pathWithSearch, signal, reg
705
807
  applyHeadEntriesToDocument(headStore.entries);
706
808
  host.innerHTML = `<div data-router-view>${html}</div>`;
707
809
  });
708
- return () => {};
810
+ return noopRouteHandle();
709
811
  }
710
812
  const html = await withHeadStore(headStore, () => island.hydratable(props, {
711
813
  name,
@@ -716,7 +818,7 @@ async function mountRouteWithHydration(island, host, pathWithSearch, signal, reg
716
818
  applyHeadEntriesToDocument(headStore.entries);
717
819
  host.innerHTML = `<div data-router-view>${html}</div>`;
718
820
  const islandHost = host.querySelector(`[data-ilha="${name}"]`);
719
- return islandHost ? island.mount(islandHost) : () => {};
821
+ return islandHost ? mountIslandWithHandle(island, islandHost) : noopRouteHandle();
720
822
  });
721
823
  }
722
824
  /** Render + mount a `+error` boundary island for a failed loader. */
@@ -960,7 +1062,12 @@ function navigate(to, opts = {}) {
960
1062
  }
961
1063
  _lastNavKey = currentNavKey();
962
1064
  syncRouteFromLocation();
963
- if (opts.scroll !== false) scrollAfterNavigate(adapter.readLocation().hash);
1065
+ if (opts.scroll !== false) {
1066
+ const dest = adapter.readLocation();
1067
+ const fromMatch = findRoute(_rou3, "GET", cur.pathname);
1068
+ const destMatch = findRoute(_rou3, "GET", dest.pathname);
1069
+ if (!(fromMatch?.data?.island != null && destMatch?.data?.island === fromMatch.data.island) || dest.hash && dest.hash !== "#") scrollAfterNavigate(dest.hash);
1070
+ }
964
1071
  runAfterNavigateHooks({
965
1072
  from: current,
966
1073
  to,
@@ -1534,11 +1641,23 @@ function router(options = {}) {
1534
1641
  if (getHistoryMode() === "hash") console.warn("[ilha-router] mount({ hydrate: true }) was called in hash mode. SSR + hydration assumes the server can render the active route, but in hash mode the server only ever sees the document URL. Use plain SPA mode (`mount(target)` without `hydrate: true`) for hash-mode apps.");
1535
1642
  const viewHost = host.querySelector("[data-router-view]") ?? host;
1536
1643
  let currentMountedIsland = activeIsland();
1644
+ let currentMountedPath = routePath() + routeSearch();
1645
+ let viewHandle = null;
1646
+ const adoptHydratedHandle = () => {
1647
+ const el = viewHost.querySelector("[data-ilha]");
1648
+ if (!el) return null;
1649
+ const h = ISLAND_MOUNT_HANDLES.get(el);
1650
+ return h ? {
1651
+ unmount: () => void h.unmount(),
1652
+ updateProps: (p) => h.updateProps(p)
1653
+ } : null;
1654
+ };
1537
1655
  const reverseRegistry = registry ? buildReverseRegistry(registry) : void 0;
1538
1656
  let navVersion = 0;
1539
1657
  const NavHandler = ilha.render(() => {
1540
1658
  const current = activeIsland();
1541
- if (current !== currentMountedIsland) {
1659
+ const pathWithSearch = routePath() + routeSearch();
1660
+ if (current !== currentMountedIsland || pathWithSearch !== currentMountedPath) {
1542
1661
  const thisNav = ++navVersion;
1543
1662
  navAbort?.abort();
1544
1663
  navAbort = new AbortController();
@@ -1546,11 +1665,20 @@ function router(options = {}) {
1546
1665
  queueMicrotask(async () => {
1547
1666
  if (thisNav !== navVersion) return;
1548
1667
  const settle = beginNavigation();
1549
- unmountView?.();
1550
- unmountView = null;
1551
1668
  try {
1552
- const loc = getAdapter().readLocation();
1553
- unmountView = await mountRouteWithHydration(current, viewHost, loc.pathname + loc.search, signal, registry, reverseRegistry);
1669
+ if (current !== null && current === currentMountedIsland) {
1670
+ const handle = viewHandle ?? adoptHydratedHandle();
1671
+ if (handle?.updateProps) {
1672
+ if (await updateRouteInPlace(handle, pathWithSearch, signal) === "updated") {
1673
+ viewHandle = handle;
1674
+ currentMountedPath = pathWithSearch;
1675
+ return;
1676
+ }
1677
+ }
1678
+ }
1679
+ viewHandle?.unmount();
1680
+ viewHandle = null;
1681
+ viewHandle = await mountRouteWithHydration(current, viewHost, pathWithSearch, signal, registry, reverseRegistry);
1554
1682
  } catch (e) {
1555
1683
  if (e?.name === "AbortError") return;
1556
1684
  console.error("[ilha-router] navigation failed:", e);
@@ -1560,6 +1688,7 @@ function router(options = {}) {
1560
1688
  settle();
1561
1689
  }
1562
1690
  currentMountedIsland = current;
1691
+ currentMountedPath = pathWithSearch;
1563
1692
  });
1564
1693
  }
1565
1694
  return "";
@@ -1580,8 +1709,8 @@ function router(options = {}) {
1580
1709
  navAbort = ac;
1581
1710
  try {
1582
1711
  const um = await mountRouteWithHydration(island, viewHost, pathWithSearch, ac.signal, registry, reverseRegistry);
1583
- if (thisNav === navVersion) unmountView = um;
1584
- else um();
1712
+ if (thisNav === navVersion) viewHandle = um;
1713
+ else um.unmount();
1585
1714
  } catch (e) {
1586
1715
  if (e?.name !== "AbortError") console.error("[ilha-router] initial client loader render failed:", e);
1587
1716
  }
@@ -1607,12 +1736,26 @@ function router(options = {}) {
1607
1736
  const settle = beginNavigation();
1608
1737
  try {
1609
1738
  const loc = getAdapter().readLocation();
1610
- const um = await mountRouteWithHydration(island, viewHost, loc.pathname + loc.search, ac.signal, registry, reverseRegistry);
1739
+ const pathWithSearch = loc.pathname + loc.search;
1740
+ if (island !== null && island === currentMountedIsland) {
1741
+ const handle = viewHandle ?? adoptHydratedHandle();
1742
+ if (handle?.updateProps) {
1743
+ if (await updateRouteInPlace(handle, pathWithSearch, ac.signal) === "updated") {
1744
+ if (thisNav === navVersion) {
1745
+ viewHandle = handle;
1746
+ currentMountedPath = pathWithSearch;
1747
+ }
1748
+ return;
1749
+ }
1750
+ }
1751
+ }
1752
+ const um = await mountRouteWithHydration(island, viewHost, pathWithSearch, ac.signal, registry, reverseRegistry);
1611
1753
  if (thisNav === navVersion) {
1612
- unmountView?.();
1613
- unmountView = um;
1754
+ viewHandle?.unmount();
1755
+ viewHandle = um;
1614
1756
  currentMountedIsland = island;
1615
- } else um();
1757
+ currentMountedPath = pathWithSearch;
1758
+ } else um.unmount();
1616
1759
  } catch (e) {
1617
1760
  if (e?.name !== "AbortError") console.error("[ilha-router] invalidate failed:", e);
1618
1761
  } finally {
@@ -1626,7 +1769,7 @@ function router(options = {}) {
1626
1769
  navAbort?.abort();
1627
1770
  unmountNavHandler();
1628
1771
  navHost.remove();
1629
- unmountView?.();
1772
+ viewHandle?.unmount();
1630
1773
  _linkCleanup?.();
1631
1774
  _navChangeCleanup?.();
1632
1775
  _linkCleanup = null;
@@ -1634,8 +1777,10 @@ function router(options = {}) {
1634
1777
  if (prevScrollRestoration !== null) history.scrollRestoration = prevScrollRestoration;
1635
1778
  };
1636
1779
  }
1637
- let unmountIsland = null;
1780
+ let mountedHandle = null;
1781
+ let mountedHandleIsland = null;
1638
1782
  let currentMountedIsland = null;
1783
+ let currentMountedPath = null;
1639
1784
  let navVersion = 0;
1640
1785
  unmountView = RouterView.mount(host);
1641
1786
  /**
@@ -1644,12 +1789,21 @@ function router(options = {}) {
1644
1789
  * render would have no access to loader data.
1645
1790
  */
1646
1791
  async function mountActiveIsland(island, signal) {
1647
- unmountIsland?.();
1648
- unmountIsland = null;
1792
+ const sameIsland = island !== null && island === mountedHandleIsland && mountedHandle?.updateProps != null;
1793
+ const teardown = () => {
1794
+ mountedHandle?.unmount();
1795
+ mountedHandle = null;
1796
+ mountedHandleIsland = null;
1797
+ };
1798
+ if (!sameIsland) teardown();
1649
1799
  currentMountedIsland = island;
1800
+ currentMountedPath = routePath() + routeSearch();
1650
1801
  if (!island) {
1651
1802
  const nfHost = host?.querySelector("[data-router-not-found]");
1652
- if (_notFound && nfHost) unmountIsland = _notFound.mount(nfHost);
1803
+ if (_notFound && nfHost) mountedHandle = {
1804
+ unmount: _notFound.mount(nfHost),
1805
+ updateProps: null
1806
+ };
1653
1807
  return;
1654
1808
  }
1655
1809
  const viewHost = host?.querySelector("[data-router-view]");
@@ -1666,9 +1820,13 @@ function router(options = {}) {
1666
1820
  return;
1667
1821
  }
1668
1822
  if (result.kind === "error") {
1823
+ if (sameIsland) teardown();
1669
1824
  const boundary = clientMatch?.data?.errorHandler;
1670
1825
  if (boundary) {
1671
- unmountIsland = await withViewSwap(() => mountLoaderErrorBoundary(boundary, viewHost, result.status, result.message));
1826
+ mountedHandle = await withViewSwap(() => ({
1827
+ unmount: mountLoaderErrorBoundary(boundary, viewHost, result.status, result.message),
1828
+ updateProps: null
1829
+ }));
1672
1830
  return;
1673
1831
  }
1674
1832
  const escaped = escapeHtml(result.message);
@@ -1678,13 +1836,19 @@ function router(options = {}) {
1678
1836
  return;
1679
1837
  }
1680
1838
  const props = result.kind === "data" ? result.data : {};
1839
+ if (sameIsland && mountedHandle?.updateProps) {
1840
+ if (result.kind === "data" && result.headEntries?.length) applyHeadEntriesToDocument([...result.headEntries]);
1841
+ mountedHandle.updateProps(props);
1842
+ return;
1843
+ }
1681
1844
  const headStore = { entries: [...result.kind === "data" ? result.headEntries ?? [] : []] };
1682
1845
  const html = await withHeadStore(headStore, () => island.toString(props));
1683
- unmountIsland = await withViewSwap(() => {
1846
+ mountedHandle = await withViewSwap(() => {
1684
1847
  applyHeadEntriesToDocument(headStore.entries);
1685
1848
  viewHost.innerHTML = html;
1686
- return island.mount(viewHost, props);
1849
+ return mountIslandWithHandle(island, viewHost, props);
1687
1850
  });
1851
+ mountedHandleIsland = island;
1688
1852
  }
1689
1853
  navAbort = new AbortController();
1690
1854
  mountActiveIsland(activeIsland(), navAbort.signal).catch((e) => {
@@ -1693,7 +1857,8 @@ function router(options = {}) {
1693
1857
  });
1694
1858
  const NavHandler = ilha.render(() => {
1695
1859
  const current = activeIsland();
1696
- if (current !== currentMountedIsland) {
1860
+ const pathWithSearch = routePath() + routeSearch();
1861
+ if (current !== currentMountedIsland || pathWithSearch !== currentMountedPath) {
1697
1862
  const thisNav = ++navVersion;
1698
1863
  navAbort?.abort();
1699
1864
  navAbort = new AbortController();
@@ -1731,7 +1896,7 @@ function router(options = {}) {
1731
1896
  ++navVersion;
1732
1897
  _revalidate = null;
1733
1898
  navAbort?.abort();
1734
- unmountIsland?.();
1899
+ mountedHandle?.unmount();
1735
1900
  unmountNavHandler();
1736
1901
  navHost.remove();
1737
1902
  unmountView?.();
package/dist/ssr.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as LOADER_ENDPOINT } from "./src-C2_zEYnV.js";
1
+ import { t as LOADER_ENDPOINT } from "./src-Dfq3JPRz.js";
2
2
  import { pageRouter, registry } from "ilha:pages/server";
3
3
  import "ilha:loaders";
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ilha/router",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "A tiny SPA router for Ilha",
5
5
  "keywords": [
6
6
  "frontend",
@@ -68,10 +68,10 @@
68
68
  "unplugin": "3.3.0"
69
69
  },
70
70
  "devDependencies": {
71
- "ilha": "0.9.2",
71
+ "ilha": "0.9.3",
72
72
  "vite": "^8.1.3"
73
73
  },
74
74
  "peerDependencies": {
75
- "ilha": ">=0.9.2"
75
+ "ilha": ">=0.9.3"
76
76
  }
77
77
  }