sibujs 4.14.0 → 4.15.0

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.
Files changed (93) hide show
  1. package/dist/browser.cjs +297 -45
  2. package/dist/browser.js +6 -7
  3. package/dist/build.cjs +2189 -573
  4. package/dist/build.js +16 -15
  5. package/dist/cdn.dev.global.js +13 -12
  6. package/dist/cdn.full.dev.global.js +14 -13
  7. package/dist/cdn.full.global.js +12 -12
  8. package/dist/cdn.global.js +12 -12
  9. package/dist/{chunk-RVR5NPFL.js → chunk-2VKTFA7C.js} +1 -1
  10. package/dist/{chunk-7ILEHTVX.js → chunk-4AEFGRYW.js} +1 -1
  11. package/dist/{chunk-4TQDTWWN.js → chunk-6IV5ZRG6.js} +5 -7
  12. package/dist/{chunk-GVAMIU6K.js → chunk-75EAQH3U.js} +2 -2
  13. package/dist/{chunk-TLS4NVIB.js → chunk-7JO7DPSS.js} +4 -4
  14. package/dist/{chunk-SVULV7O4.js → chunk-AQH7WDSE.js} +6 -8
  15. package/dist/{chunk-S37PZD6I.js → chunk-BF3WBJJQ.js} +60 -10
  16. package/dist/{chunk-LJMX4SQ7.js → chunk-BG4BP44S.js} +3 -3
  17. package/dist/{chunk-N7A6FKWW.js → chunk-D44TAGCT.js} +2 -2
  18. package/dist/{chunk-CCKUNHVU.js → chunk-DAEXQJQA.js} +1 -1
  19. package/dist/chunk-DNCQDFJJ.js +22 -0
  20. package/dist/{chunk-BKZ5N6NK.js → chunk-DNCT2UXB.js} +4 -4
  21. package/dist/{chunk-BOYKGGVD.js → chunk-DRWOD7RZ.js} +4 -4
  22. package/dist/chunk-DS5V2PRE.js +64 -0
  23. package/dist/{chunk-VM2U5LEL.js → chunk-EHSJESRN.js} +595 -123
  24. package/dist/{chunk-MIRKTVH4.js → chunk-GFM5WWPU.js} +252 -5
  25. package/dist/{chunk-52XFPGSN.js → chunk-HTPOIAAL.js} +1 -1
  26. package/dist/{chunk-3HEX6QJQ.js → chunk-I2R5QALY.js} +2 -4
  27. package/dist/{chunk-4FJSAUKB.js → chunk-IUBV5WO5.js} +1 -1
  28. package/dist/{chunk-A454RAEK.js → chunk-KIDETDMU.js} +5 -7
  29. package/dist/{chunk-JTFQ7IL4.js → chunk-KPAXG5IR.js} +7 -9
  30. package/dist/{chunk-AE32RGWX.js → chunk-LGUXYHTZ.js} +3 -3
  31. package/dist/{chunk-Y4LUM2XS.js → chunk-LQWLPQOK.js} +6 -8
  32. package/dist/{chunk-CNPT6NKV.js → chunk-MINLMJUY.js} +48 -9
  33. package/dist/{chunk-BNXRWZZV.js → chunk-OURTGOJT.js} +8 -10
  34. package/dist/{chunk-F67F77IA.js → chunk-TQ24Y3IV.js} +2 -2
  35. package/dist/{chunk-2TTRW2TE.js → chunk-UM3M5PXS.js} +8 -8
  36. package/dist/{chunk-CJ7KS3DF.js → chunk-VSTX3XSX.js} +205 -15
  37. package/dist/{chunk-HQIAL7F2.js → chunk-XPDZ2LDR.js} +2 -2
  38. package/dist/{chunk-UUIKJJIK.js → chunk-Y7JM7GY2.js} +5 -7
  39. package/dist/{chunk-QS6GMWDD.js → chunk-YAFITBOK.js} +1269 -162
  40. package/dist/data.cjs +285 -33
  41. package/dist/data.js +6 -7
  42. package/dist/devtools.cjs +289 -39
  43. package/dist/devtools.js +5 -6
  44. package/dist/{dispose-EIHHHDC7.js → dispose-3WDBYJMH.js} +3 -1
  45. package/dist/ecosystem.cjs +345 -61
  46. package/dist/ecosystem.d.cts +1 -1
  47. package/dist/ecosystem.d.ts +1 -1
  48. package/dist/ecosystem.js +10 -10
  49. package/dist/extras.cjs +568 -248
  50. package/dist/extras.d.cts +1 -1
  51. package/dist/extras.d.ts +1 -1
  52. package/dist/extras.js +27 -27
  53. package/dist/index.cjs +2134 -515
  54. package/dist/index.d.cts +628 -211
  55. package/dist/index.d.ts +628 -211
  56. package/dist/index.js +22 -15
  57. package/dist/motion.cjs +286 -30
  58. package/dist/motion.js +4 -4
  59. package/dist/patterns.cjs +301 -43
  60. package/dist/patterns.js +6 -6
  61. package/dist/performance.cjs +286 -36
  62. package/dist/performance.js +9 -9
  63. package/dist/plugins.cjs +1237 -291
  64. package/dist/plugins.d.cts +3 -1
  65. package/dist/plugins.d.ts +3 -1
  66. package/dist/plugins.js +20 -19
  67. package/dist/{renderRoute-fxgsm2Ir.d.ts → renderRoute-B84MONaO.d.cts} +2 -2
  68. package/dist/{renderRoute-DP20XwMS.d.cts → renderRoute-_yxPuEQY.d.ts} +2 -2
  69. package/dist/{routeHead-CE9SV-cH.d.cts → routeHead-DLxlq15W.d.cts} +94 -0
  70. package/dist/{routeHead-DzVxPviY.d.ts → routeHead-w8nANZ3M.d.ts} +94 -0
  71. package/dist/{ssr-NECEU4X4.js → ssr-EF3IUHTE.js} +6 -6
  72. package/dist/ssr-dom.cjs +64 -9
  73. package/dist/ssr-dom.d.cts +2 -0
  74. package/dist/ssr-dom.d.ts +2 -0
  75. package/dist/ssr-dom.js +2 -1
  76. package/dist/ssr.cjs +850 -170
  77. package/dist/ssr.d.cts +5 -3
  78. package/dist/ssr.d.ts +5 -3
  79. package/dist/ssr.js +16 -15
  80. package/dist/{tagFactory-DkytnJws.d.cts → tagFactory-BfEh69VF.d.cts} +7 -0
  81. package/dist/{tagFactory-Bnd_Nph9.d.ts → tagFactory-pSYiLYyh.d.ts} +7 -0
  82. package/dist/testing.cjs +817 -132
  83. package/dist/testing.d.cts +4 -2
  84. package/dist/testing.d.ts +4 -2
  85. package/dist/testing.js +18 -17
  86. package/dist/track-core-0edROAF5.d.ts +187 -0
  87. package/dist/track-core-DN5fYLWi.d.cts +187 -0
  88. package/dist/ui.cjs +293 -43
  89. package/dist/ui.js +10 -12
  90. package/dist/widgets.cjs +285 -27
  91. package/dist/widgets.js +7 -7
  92. package/package.json +1 -1
  93. package/dist/chunk-7ZHH77QA.js +0 -9
@@ -1,23 +1,28 @@
1
1
  import {
2
2
  acquireTitle
3
- } from "./chunk-52XFPGSN.js";
3
+ } from "./chunk-HTPOIAAL.js";
4
+ import {
5
+ commitWhenActive
6
+ } from "./chunk-DS5V2PRE.js";
4
7
  import {
5
8
  STATIC_VALUE_RESOLVER,
6
9
  planHeadElementEntry,
7
10
  planMetaEntry,
8
11
  recordServerHead,
9
12
  serializeHeadEntry
10
- } from "./chunk-BOYKGGVD.js";
13
+ } from "./chunk-DRWOD7RZ.js";
11
14
  import {
12
15
  appendChildren,
16
+ beginLoading,
17
+ registerNodesRange,
13
18
  resolveClassValue
14
- } from "./chunk-CJ7KS3DF.js";
19
+ } from "./chunk-VSTX3XSX.js";
15
20
  import {
16
21
  setSafeAttribute
17
- } from "./chunk-HQIAL7F2.js";
22
+ } from "./chunk-XPDZ2LDR.js";
18
23
  import {
19
24
  effect
20
- } from "./chunk-AE32RGWX.js";
25
+ } from "./chunk-LGUXYHTZ.js";
21
26
  import {
22
27
  sanitizeUrl,
23
28
  stripControlChars
@@ -28,18 +33,21 @@ import {
28
33
  } from "./chunk-UYTYNCMZ.js";
29
34
  import {
30
35
  signal
31
- } from "./chunk-4FJSAUKB.js";
36
+ } from "./chunk-IUBV5WO5.js";
32
37
  import {
33
38
  bindOwnerScope,
39
+ captureFrozen,
34
40
  getOwnerScope,
41
+ holdSubtree,
42
+ reactiveBinding,
35
43
  runWithOwnerScope,
36
- track,
44
+ structuralBinding,
37
45
  untracked
38
- } from "./chunk-MIRKTVH4.js";
46
+ } from "./chunk-GFM5WWPU.js";
39
47
  import {
40
48
  dispose,
41
49
  registerDisposer
42
- } from "./chunk-S37PZD6I.js";
50
+ } from "./chunk-BF3WBJJQ.js";
43
51
  import {
44
52
  captureScope,
45
53
  devWarn,
@@ -929,7 +937,14 @@ var _ComponentLoader = class _ComponentLoader {
929
937
  * is a configuration error, not something to chase.
930
938
  */
931
939
  async instantiateFactory(component, route2, routePath, scope, depth = 0) {
932
- const build = () => scope ? runWithOwnerScope(scope, component) : component();
940
+ const gate = scope?.gate;
941
+ if (gate) {
942
+ while (gate.held()) if (!await gate.until()) throw gate.cancellation();
943
+ if (scope?.current?.() === false) throw gate.cancellation();
944
+ }
945
+ const owner = scope?.owner;
946
+ const invoke = () => scope ? runWithOwnerScope(scope, component) : component();
947
+ const build = owner ? () => owner.run(invoke) : invoke;
933
948
  const result = scope?.adopt ? inScope(scope.adopt, build) : build();
934
949
  const value = this.isThenable(result) ? await result : result;
935
950
  if (this.isElement(value)) return value;
@@ -1138,7 +1153,7 @@ var _SibuRouter = class _SibuRouter {
1138
1153
  return this.loadedPromise;
1139
1154
  }
1140
1155
  createInitialRoute() {
1141
- return {
1156
+ const route2 = {
1142
1157
  path: "/",
1143
1158
  params: {},
1144
1159
  query: {},
@@ -1147,6 +1162,8 @@ var _SibuRouter = class _SibuRouter {
1147
1162
  meta: {},
1148
1163
  matched: []
1149
1164
  };
1165
+ bootstrapRoutes.add(route2);
1166
+ return route2;
1150
1167
  }
1151
1168
  handleLocationChange(skipHistory = true, initialResolution = false) {
1152
1169
  const path = this.getCurrentPath();
@@ -1187,8 +1204,31 @@ var _SibuRouter = class _SibuRouter {
1187
1204
  matched
1188
1205
  };
1189
1206
  }
1207
+ /**
1208
+ * Once no navigation is in flight: if the route is still the placeholder
1209
+ * the router started with — every navigation that could have replaced it
1210
+ * was cancelled by a guard, failed, or was a duplicate — that IS the
1211
+ * outcome, and the outlets that rendered nothing for it settle (see
1212
+ * `settleEmpty`). Checked when a navigation has definitively completed,
1213
+ * never from `isReady` (set before the guards run). A navigation still
1214
+ * pending — a newer one, or a guard that never resolves — keeps them
1215
+ * pending; an outlet's enter is bounded by its own deadline then.
1216
+ */
1217
+ settleBootstrap() {
1218
+ if (this.destroyed || this.navigator.isNavigating) return;
1219
+ const current = untracked(() => this.currentRouteGetter());
1220
+ if (!bootstrapRoutes.delete(current)) return;
1221
+ const waiting = bootstrapWaiters.get(current);
1222
+ bootstrapWaiters.delete(current);
1223
+ if (waiting) for (const settle of waiting) settle();
1224
+ }
1190
1225
  // Public API
1191
1226
  async navigate(to, options = {}) {
1227
+ const result = await this.attemptNavigation(to, options);
1228
+ this.settleBootstrap();
1229
+ return result;
1230
+ }
1231
+ async attemptNavigation(to, options) {
1192
1232
  try {
1193
1233
  await this.navigator.navigate(async (signal2) => {
1194
1234
  const targetPath = this.resolvePath(to);
@@ -1442,6 +1482,13 @@ var _SibuRouter = class _SibuRouter {
1442
1482
  /**
1443
1483
  * Create exactly one real instance from an already-resolved plan. With a
1444
1484
  * `scope`, the factory runs in it (see `RouteScope`).
1485
+ *
1486
+ * When the scope belongs to an outlet whose branch is paused (playing a
1487
+ * leave it may still be taken back from), a lazy module that resolves does
1488
+ * not run its factory yet: the promise stays pending and continues once the
1489
+ * branch resumes. If the branch ends, or the outlet's pass was superseded
1490
+ * meanwhile, it rejects with a `DOMException` named `"AbortError"`. Genuine
1491
+ * loader and component failures reject with their own error, as before.
1445
1492
  */
1446
1493
  async instantiateComponent(plan, route2, routePath, scope) {
1447
1494
  return prerenderWait(this.loader.instantiate(plan, route2, routePath, scope), `router:render ${route2.path}`);
@@ -1690,6 +1737,21 @@ var RouteScope = class {
1690
1737
  constructor(route2) {
1691
1738
  /** Creation scope the instance is built in (adopting hydration); see `adoptScope`. */
1692
1739
  this.adopt = null;
1740
+ /**
1741
+ * The outlet's pause gate: while the branch holding the outlet is paused (it
1742
+ * began a leave and may still be taken back), a lazy module that resolves
1743
+ * does not get its factory invoked yet; instantiation waits for the branch
1744
+ * to resume, and is cancelled if it ends.
1745
+ */
1746
+ this.gate = null;
1747
+ /** Whether the outlet pass this instance is for is still the current one. */
1748
+ this.current = null;
1749
+ /**
1750
+ * The instance's structural owner, nested under the outlet's: its factory
1751
+ * runs as it, so what it creates is paused with the branch holding the
1752
+ * outlet, and ends (`lifecycleScope()` work included) with the instance.
1753
+ */
1754
+ this.owner = null;
1693
1755
  this.released = false;
1694
1756
  const [get, set] = signal(route2);
1695
1757
  this.get = get;
@@ -1716,6 +1778,112 @@ var RouteScope = class {
1716
1778
  queueMicrotask(() => this.set(null));
1717
1779
  }
1718
1780
  };
1781
+ var NOOP = () => {
1782
+ };
1783
+ var bootstrapRoutes = /* @__PURE__ */ new WeakSet();
1784
+ var bootstrapWaiters = /* @__PURE__ */ new WeakMap();
1785
+ function settleEmpty(route2, loaded, current) {
1786
+ if (!bootstrapRoutes.has(route2)) {
1787
+ loaded.done();
1788
+ return;
1789
+ }
1790
+ let waiting = bootstrapWaiters.get(route2);
1791
+ if (waiting === void 0) bootstrapWaiters.set(route2, waiting = /* @__PURE__ */ new Set());
1792
+ waiting.add(() => {
1793
+ if (current()) loaded.done();
1794
+ });
1795
+ }
1796
+ var cancellations = /* @__PURE__ */ new WeakSet();
1797
+ function pauseGate(frozen) {
1798
+ let closed = false;
1799
+ const pending = /* @__PURE__ */ new Set();
1800
+ return {
1801
+ held: () => closed || frozen(),
1802
+ until: () => {
1803
+ if (closed) return Promise.resolve(false);
1804
+ return new Promise((resolve) => {
1805
+ const settle = (active) => {
1806
+ pending.delete(cancel);
1807
+ resolve(active && !closed);
1808
+ };
1809
+ const drop = frozen.wait(
1810
+ () => settle(true),
1811
+ () => settle(false)
1812
+ );
1813
+ const cancel = () => {
1814
+ drop();
1815
+ settle(false);
1816
+ };
1817
+ pending.add(cancel);
1818
+ });
1819
+ },
1820
+ cancellation: () => {
1821
+ const error = new DOMException("Route instantiation cancelled: its branch was released", "AbortError");
1822
+ cancellations.add(error);
1823
+ return error;
1824
+ },
1825
+ close: () => {
1826
+ closed = true;
1827
+ for (const cancel of [...pending]) cancel();
1828
+ },
1829
+ run: frozen.run,
1830
+ frozen,
1831
+ closed: () => closed,
1832
+ onClose: (fn) => {
1833
+ pending.add(fn);
1834
+ return () => {
1835
+ pending.delete(fn);
1836
+ };
1837
+ }
1838
+ };
1839
+ }
1840
+ function whenActive(gate, fn) {
1841
+ if (!gate.held()) fn();
1842
+ else
1843
+ void gate.until().then((active) => {
1844
+ if (active) whenActive(gate, fn);
1845
+ });
1846
+ }
1847
+ function commitPass(gate, c) {
1848
+ const frozen = gate.frozen;
1849
+ const held = {
1850
+ state: frozen.state,
1851
+ wait: (onActive, onEnd) => {
1852
+ let live = true;
1853
+ const later = (fn) => () => {
1854
+ void Promise.resolve().then(() => {
1855
+ if (live) fn();
1856
+ });
1857
+ };
1858
+ const drop = frozen.wait(later(onActive), later(onEnd));
1859
+ return () => {
1860
+ live = false;
1861
+ drop();
1862
+ };
1863
+ }
1864
+ };
1865
+ return new Promise((resolve, reject) => {
1866
+ const slot = { cancel: NOOP };
1867
+ const untrack = gate.onClose(() => slot.cancel());
1868
+ const settle = (fn) => () => {
1869
+ untrack();
1870
+ try {
1871
+ fn();
1872
+ resolve();
1873
+ } catch (error) {
1874
+ reject(error);
1875
+ }
1876
+ };
1877
+ commitWhenActive(slot, {
1878
+ held,
1879
+ node: c.node,
1880
+ owned: () => !gate.closed() && c.owned(),
1881
+ teardown: c.teardown,
1882
+ insert: settle(c.insert),
1883
+ discard: settle(c.discard)
1884
+ });
1885
+ });
1886
+ }
1719
1887
  function activeRouteScope() {
1720
1888
  const scope = getOwnerScope();
1721
1889
  return scope instanceof RouteScope ? scope : null;
@@ -1729,14 +1897,27 @@ function scopedRoute(router2) {
1729
1897
  return router2.currentRoute;
1730
1898
  }
1731
1899
  var instanceScopes = /* @__PURE__ */ new WeakMap();
1732
- async function instantiateScoped(router2, plan, routeDef, routePath, route2, adopt = null) {
1900
+ async function instantiateScoped(router2, plan, routeDef, routePath, route2, adopt = null, gate = null, current = null) {
1733
1901
  const scope = new RouteScope(route2);
1734
1902
  scope.adopt = adopt;
1735
- const node = await router2.instantiateComponent(plan, routeDef, routePath, scope);
1903
+ scope.gate = gate;
1904
+ scope.current = current;
1905
+ const owner = gate ? gate.run(() => structuralBinding(NOOP)) : structuralBinding(NOOP);
1906
+ scope.owner = owner;
1907
+ let node;
1908
+ try {
1909
+ node = await router2.instantiateComponent(plan, routeDef, routePath, scope);
1910
+ } catch (error) {
1911
+ owner.stop();
1912
+ throw error;
1913
+ }
1736
1914
  if (node) {
1737
1915
  instanceScopes.set(node, scope);
1738
- registerDisposer(node, () => scope.release());
1739
- }
1916
+ registerDisposer(node, () => {
1917
+ owner.stop();
1918
+ scope.release();
1919
+ });
1920
+ } else owner.stop();
1740
1921
  return node;
1741
1922
  }
1742
1923
  function updateInstanceRoute(node, route2) {
@@ -1835,6 +2016,8 @@ function instanceKey(owner, records, route2) {
1835
2016
  function Route() {
1836
2017
  const anchor = document.createComment("route-outlet");
1837
2018
  const adoptScope = captureScope();
2019
+ const frozen = captureFrozen();
2020
+ const gate = pauseGate(frozen);
1838
2021
  let currentNode = null;
1839
2022
  let loadingNode = null;
1840
2023
  let errorNode = null;
@@ -1842,6 +2025,7 @@ function Route() {
1842
2025
  let currentTopRoute = null;
1843
2026
  let currentKey = null;
1844
2027
  let routeTorn = false;
2028
+ const loaded = beginLoading(anchor);
1845
2029
  const commitTarget = (seq) => routeTorn || seq !== navSeq ? null : anchor.parentNode;
1846
2030
  const retryIfDetached = (seq) => {
1847
2031
  if (!routeTorn && seq === navSeq && !anchor.parentNode) attachWait.request();
@@ -1886,24 +2070,54 @@ function Route() {
1886
2070
  loadingNode = null;
1887
2071
  }
1888
2072
  };
1889
- const showError = (error, routeDef) => {
2073
+ const showError = (seq, error, routeDef) => {
1890
2074
  if (!anchor.parentNode) return;
1891
- cleanupNodes();
1892
- errorNode = document.createElement("div");
1893
- errorNode.className = "route-error";
1894
- errorNode.setAttribute("role", "alert");
1895
- errorNode.setAttribute("aria-live", "assertive");
2075
+ const view = errorView(error, routeDef);
2076
+ void commitPass(gate, {
2077
+ node: view,
2078
+ owned: () => !routeTorn && seq === navSeq,
2079
+ discard: () => dispose(view),
2080
+ teardown: () => {
2081
+ if (!anchor.parentNode || !(currentNode || loadingNode || errorNode)) return false;
2082
+ cleanupNodes();
2083
+ return true;
2084
+ },
2085
+ insert: () => {
2086
+ const parent = anchor.parentNode;
2087
+ if (!parent) {
2088
+ dispose(view);
2089
+ unshownFailure = { seq, error, routeDef };
2090
+ attachWait.request();
2091
+ return;
2092
+ }
2093
+ errorNode = view;
2094
+ try {
2095
+ parent.insertBefore(view, anchor.nextSibling);
2096
+ } catch (err) {
2097
+ if (errorNode === view) errorNode = null;
2098
+ release(view);
2099
+ throw err;
2100
+ }
2101
+ if (!routeTorn && seq === navSeq) loaded.done();
2102
+ }
2103
+ }).catch((err) => console.error("[Route] Error view failed:", err));
2104
+ };
2105
+ const errorView = (error, routeDef) => {
2106
+ const errorNode2 = document.createElement("div");
2107
+ errorNode2.className = "route-error";
2108
+ errorNode2.setAttribute("role", "alert");
2109
+ errorNode2.setAttribute("aria-live", "assertive");
1896
2110
  if (routeDef && "component" in routeDef) {
1897
2111
  const src = routeDef.component.toString();
1898
2112
  const importMatch = src.match(/import\(["']([^"']+)["']\)/);
1899
2113
  if (importMatch) {
1900
- errorNode.setAttribute("data-component-source", importMatch[1]);
2114
+ errorNode2.setAttribute("data-component-source", importMatch[1]);
1901
2115
  }
1902
2116
  if (routeDef.component.name) {
1903
- errorNode.setAttribute("data-component-name", routeDef.component.name);
2117
+ errorNode2.setAttribute("data-component-name", routeDef.component.name);
1904
2118
  }
1905
2119
  }
1906
- errorNode.__routeError = error;
2120
+ errorNode2.__routeError = error;
1907
2121
  const title = document.createElement("h3");
1908
2122
  title.textContent = "Route Error";
1909
2123
  title.className = "route-error-title";
@@ -1922,10 +2136,10 @@ function Route() {
1922
2136
  };
1923
2137
  retryButton.addEventListener("click", onRetryClick);
1924
2138
  registerDisposer(retryButton, () => retryButton.removeEventListener("click", onRetryClick));
1925
- errorNode.appendChild(title);
1926
- errorNode.appendChild(message);
1927
- errorNode.appendChild(retryButton);
1928
- anchor.parentNode.insertBefore(errorNode, anchor.nextSibling);
2139
+ errorNode2.appendChild(title);
2140
+ errorNode2.appendChild(message);
2141
+ errorNode2.appendChild(retryButton);
2142
+ return errorNode2;
1929
2143
  };
1930
2144
  const update = async () => {
1931
2145
  routerRevision();
@@ -1938,12 +2152,14 @@ function Route() {
1938
2152
  currentTopRoute = null;
1939
2153
  currentKey = null;
1940
2154
  cleanupNodes();
2155
+ settleEmpty(route2, loaded, () => !routeTorn && seq === navSeq);
1941
2156
  return;
1942
2157
  }
1943
2158
  const routeDef = match.matched[0] || match.route;
1944
2159
  const key = instanceKey(routeDef, [routeDef], route2);
1945
2160
  if (routeDef === currentTopRoute && key === currentKey && currentNode) {
1946
2161
  updateInstanceRoute(currentNode, route2);
2162
+ loaded.done();
1947
2163
  return;
1948
2164
  }
1949
2165
  if ("redirect" in routeDef) {
@@ -1962,6 +2178,7 @@ function Route() {
1962
2178
  showLoading();
1963
2179
  }
1964
2180
  const plan = await _routerRef.current.loadPlan(routeDef, route2.path);
2181
+ while (gate.held()) if (!await gate.until()) return;
1965
2182
  if (!commitTarget(seq)) {
1966
2183
  retryIfDetached(seq);
1967
2184
  return;
@@ -1972,28 +2189,57 @@ function Route() {
1972
2189
  routeDef,
1973
2190
  route2.path,
1974
2191
  route2,
1975
- keyedScope(adoptScope, routeDef.path)
2192
+ keyedScope(adoptScope, routeDef.path),
2193
+ gate,
2194
+ () => commitTarget(seq) !== null
1976
2195
  );
1977
- const parent = commitTarget(seq);
1978
- if (!parent) {
1979
- release(node);
1980
- retryIfDetached(seq);
1981
- return;
1982
- }
1983
- if (node) {
1984
- currentTopRoute = routeDef;
1985
- currentKey = key;
1986
- cleanupNodes();
1987
- parent.insertBefore(node, anchor.nextSibling);
1988
- currentNode = node;
1989
- }
2196
+ await commitPass(gate, {
2197
+ node,
2198
+ owned: () => !routeTorn && seq === navSeq,
2199
+ discard: () => release(node),
2200
+ // The outgoing views go first — only when there is a page to
2201
+ // show, and a parent to show it in.
2202
+ teardown: () => {
2203
+ if (!node || !anchor.parentNode || !(currentNode || loadingNode || errorNode)) return false;
2204
+ cleanupNodes();
2205
+ return true;
2206
+ },
2207
+ insert: () => {
2208
+ const parent = commitTarget(seq);
2209
+ if (!parent) {
2210
+ release(node);
2211
+ retryIfDetached(seq);
2212
+ return;
2213
+ }
2214
+ if (node) {
2215
+ currentTopRoute = routeDef;
2216
+ currentKey = key;
2217
+ currentNode = node;
2218
+ try {
2219
+ parent.insertBefore(node, anchor.nextSibling);
2220
+ } catch (error) {
2221
+ if (currentNode === node) {
2222
+ currentNode = null;
2223
+ currentTopRoute = null;
2224
+ currentKey = null;
2225
+ }
2226
+ release(node);
2227
+ throw error;
2228
+ }
2229
+ }
2230
+ if (!routeTorn && seq === navSeq) loaded.done();
2231
+ }
2232
+ });
1990
2233
  } catch (error) {
2234
+ if (cancellations.has(error)) return;
1991
2235
  if (routeTorn || seq !== navSeq) return;
1992
- hideLoading();
1993
2236
  console.error("[Route] Component error:", error);
2237
+ while (gate.held()) if (!await gate.until()) return;
2238
+ if (routeTorn || seq !== navSeq) return;
2239
+ hideLoading();
1994
2240
  fail(seq, error, routeDef);
1995
2241
  }
1996
- }
2242
+ } else settleEmpty(route2, loaded, () => !routeTorn && seq === navSeq);
1997
2243
  } catch (error) {
1998
2244
  if (seq !== navSeq) return;
1999
2245
  if (routeTorn) return;
@@ -2005,16 +2251,20 @@ function Route() {
2005
2251
  const fail = (seq, error, routeDef) => {
2006
2252
  const err = error instanceof Error ? error : new Error(String(error));
2007
2253
  if (anchor.parentNode) {
2008
- showError(err, routeDef);
2254
+ showError(seq, err, routeDef);
2009
2255
  return;
2010
2256
  }
2011
2257
  unshownFailure = { seq, error: err, routeDef };
2012
2258
  attachWait.request();
2013
2259
  };
2014
2260
  const onAttached = () => {
2261
+ if (gate.held()) {
2262
+ void gate.until().then((active) => active && onAttached());
2263
+ return;
2264
+ }
2015
2265
  const failure = unshownFailure;
2016
2266
  unshownFailure = null;
2017
- if (failure && failure.seq === navSeq) showError(failure.error, failure.routeDef);
2267
+ if (failure && failure.seq === navSeq) showError(failure.seq, failure.error, failure.routeDef);
2018
2268
  else update();
2019
2269
  };
2020
2270
  let routeInitialized = false;
@@ -2024,11 +2274,16 @@ function Route() {
2024
2274
  routeInitialized = true;
2025
2275
  };
2026
2276
  const attachWait = attachRetry(anchor, onAttached);
2027
- const routeTeardown = track(wrappedUpdate);
2277
+ const routeTeardown = reactiveBinding(wrappedUpdate, anchor);
2028
2278
  if (!routeInitialized) {
2029
2279
  queueMicrotask(
2030
2280
  bindOwnerScope(() => {
2031
- if (!routeInitialized && anchor.parentNode) wrappedUpdate();
2281
+ whenActive(
2282
+ gate,
2283
+ bindOwnerScope(() => {
2284
+ if (!routeInitialized && anchor.parentNode) wrappedUpdate();
2285
+ })
2286
+ );
2032
2287
  })
2033
2288
  );
2034
2289
  }
@@ -2036,6 +2291,8 @@ function Route() {
2036
2291
  if (routeTorn) return;
2037
2292
  routeTorn = true;
2038
2293
  navSeq++;
2294
+ gate.close();
2295
+ loaded.cancel();
2039
2296
  routeTeardown();
2040
2297
  attachWait.cancel();
2041
2298
  unshownFailure = null;
@@ -2043,6 +2300,7 @@ function Route() {
2043
2300
  currentTopRoute = null;
2044
2301
  currentKey = null;
2045
2302
  };
2303
+ registerNodesRange(anchor, () => [currentNode, loadingNode, errorNode]);
2046
2304
  registerDisposer(anchor, routeCleanup);
2047
2305
  routeCleanups.push(routeCleanup);
2048
2306
  return anchor;
@@ -2052,15 +2310,42 @@ function KeepAliveRoute(options) {
2052
2310
  const anchor = document.createComment("keep-alive-route");
2053
2311
  const cache = /* @__PURE__ */ new Map();
2054
2312
  const lruOrder = [];
2313
+ const frozen = captureFrozen();
2314
+ const gate = pauseGate(frozen);
2055
2315
  const routerOpts = _routerRef.current["options"];
2056
2316
  const keepAliveOpt = routerOpts.keepAlive;
2057
2317
  const maxCache = options?.max ?? (typeof keepAliveOpt === "number" ? keepAliveOpt : 20);
2058
2318
  const includeNames = options?.include ?? (Array.isArray(keepAliveOpt) ? keepAliveOpt : void 0);
2319
+ const suspendInactive = options?.suspendInactive === true;
2320
+ const suspended = /* @__PURE__ */ new Map();
2321
+ const suspend = (node) => {
2322
+ if (!suspendInactive || suspended.has(node)) return;
2323
+ const owner = instanceScopes.get(node)?.owner ?? null;
2324
+ owner?.freeze?.();
2325
+ suspended.set(node, { owner, unhold: holdSubtree(node) });
2326
+ };
2327
+ const activate = (node) => {
2328
+ const paused = suspended.get(node);
2329
+ if (paused === void 0) return;
2330
+ suspended.delete(node);
2331
+ paused.unhold();
2332
+ paused.owner?.thaw?.();
2333
+ };
2334
+ const disposeCached = (node) => {
2335
+ const paused = suspended.get(node);
2336
+ suspended.delete(node);
2337
+ try {
2338
+ disposeOwned(node);
2339
+ } finally {
2340
+ paused?.unhold();
2341
+ }
2342
+ };
2059
2343
  let currentNode = null;
2060
2344
  let currentKey = "";
2061
2345
  let currentCached = false;
2062
2346
  let updateSeq = 0;
2063
2347
  let kaTorn = false;
2348
+ const loaded = beginLoading(anchor);
2064
2349
  const update = async () => {
2065
2350
  routerRevision();
2066
2351
  if (kaTorn || !_routerRef.current) return;
@@ -2068,7 +2353,10 @@ function KeepAliveRoute(options) {
2068
2353
  const router2 = _routerRef.current;
2069
2354
  const route2 = scopedRoute(router2);
2070
2355
  const match = router2["matcher"].match(route2.path);
2071
- if (!match) return;
2356
+ if (!match) {
2357
+ settleEmpty(route2, loaded, () => !kaTorn && seq === updateSeq);
2358
+ return;
2359
+ }
2072
2360
  const { route: routeDef } = match;
2073
2361
  if ("redirect" in routeDef) {
2074
2362
  const redirectPath = typeof routeDef.redirect === "function" ? routeDef.redirect(route2) : routeDef.redirect;
@@ -2079,11 +2367,15 @@ function KeepAliveRoute(options) {
2079
2367
  });
2080
2368
  return;
2081
2369
  }
2082
- if (!("component" in routeDef)) return;
2370
+ if (!("component" in routeDef)) {
2371
+ settleEmpty(route2, loaded, () => !kaTorn && seq === updateSeq);
2372
+ return;
2373
+ }
2083
2374
  const cacheKey = JSON.stringify([route2.path, queryIdentity(route2.queryAll), route2.hash]);
2084
2375
  const shouldCache = !includeNames || routeDef.name != null && includeNames.includes(routeDef.name);
2085
2376
  if (cacheKey === currentKey && currentNode) {
2086
2377
  updateInstanceRoute(currentNode, route2);
2378
+ loaded.done();
2087
2379
  return;
2088
2380
  }
2089
2381
  if (!anchor.parentNode) {
@@ -2098,81 +2390,142 @@ function KeepAliveRoute(options) {
2098
2390
  } else {
2099
2391
  try {
2100
2392
  const plan = await router2.loadPlan(routeDef, route2.path);
2393
+ while (gate.held()) if (!await gate.until()) return;
2101
2394
  if (kaTorn || seq !== updateSeq) return;
2102
- node = await instantiateScoped(router2, plan, routeDef, route2.path, route2);
2395
+ node = await instantiateScoped(
2396
+ router2,
2397
+ plan,
2398
+ routeDef,
2399
+ route2.path,
2400
+ route2,
2401
+ null,
2402
+ gate,
2403
+ () => !kaTorn && seq === updateSeq
2404
+ );
2103
2405
  } catch (error) {
2104
- if (kaTorn || seq !== updateSeq) return;
2406
+ if (cancellations.has(error) || kaTorn || seq !== updateSeq) return;
2105
2407
  console.error("[KeepAliveRoute] Component error:", error);
2408
+ loaded.cancel();
2106
2409
  return;
2107
2410
  }
2108
- if (!node) return;
2109
- if (kaTorn || seq !== updateSeq) {
2110
- disposeOwned(node);
2411
+ if (!node) {
2412
+ if (!kaTorn && seq === updateSeq) loaded.done();
2111
2413
  return;
2112
2414
  }
2113
2415
  }
2114
- const parent = anchor.parentNode;
2115
- if (!parent) {
2416
+ await commit(seq, node, fromCache, cacheKey, shouldCache, route2);
2417
+ };
2418
+ const commit = (seq, node, fromCache, cacheKey, shouldCache, route2) => commitPass(gate, {
2419
+ node: fromCache ? null : node,
2420
+ owned: () => !kaTorn && seq === updateSeq,
2421
+ discard: () => {
2116
2422
  if (!fromCache) disposeOwned(node);
2117
- attachWait.request();
2118
- return;
2119
- }
2120
- if (currentNode && currentNode !== node) {
2121
- if (currentNode.parentNode) currentNode.parentNode.removeChild(currentNode);
2122
- if (!currentCached) disposeOwned(currentNode);
2123
- }
2124
- if (fromCache) {
2125
- updateInstanceRoute(node, route2);
2126
- const idx = lruOrder.indexOf(cacheKey);
2127
- if (idx !== -1) lruOrder.splice(idx, 1);
2128
- lruOrder.push(cacheKey);
2129
- } else if (shouldCache) {
2130
- cache.set(cacheKey, node);
2131
- lruOrder.push(cacheKey);
2132
- while (lruOrder.length > maxCache) {
2423
+ },
2424
+ // The outgoing view goes first: detached, and disposed unless it is
2425
+ // cached (then paused, with `suspendInactive`). Once only: it is no
2426
+ // longer the current view afterwards.
2427
+ teardown: () => {
2428
+ const outgoing = currentNode;
2429
+ if (outgoing === null || outgoing === node || !anchor.parentNode) return false;
2430
+ const wasCached = currentCached;
2431
+ currentNode = null;
2432
+ currentKey = "";
2433
+ currentCached = false;
2434
+ outgoing.parentNode?.removeChild(outgoing);
2435
+ if (!wasCached) disposeOwned(outgoing);
2436
+ else if (!kaTorn) suspend(outgoing);
2437
+ return true;
2438
+ },
2439
+ insert: () => {
2440
+ const parent = anchor.parentNode;
2441
+ if (!parent) {
2442
+ if (!fromCache) disposeOwned(node);
2443
+ attachWait.request();
2444
+ return;
2445
+ }
2446
+ if (fromCache) {
2447
+ updateInstanceRoute(node, route2);
2448
+ const idx = lruOrder.indexOf(cacheKey);
2449
+ if (idx !== -1) lruOrder.splice(idx, 1);
2450
+ lruOrder.push(cacheKey);
2451
+ } else if (shouldCache) {
2452
+ cache.set(cacheKey, node);
2453
+ lruOrder.push(cacheKey);
2454
+ }
2455
+ currentNode = node;
2456
+ currentCached = fromCache || shouldCache;
2457
+ currentKey = cacheKey;
2458
+ try {
2459
+ parent.insertBefore(node, anchor.nextSibling);
2460
+ } catch (error) {
2461
+ if (currentNode === node) {
2462
+ currentNode = null;
2463
+ currentKey = "";
2464
+ currentCached = false;
2465
+ }
2466
+ if (!fromCache) {
2467
+ if (cache.get(cacheKey) === node) {
2468
+ cache.delete(cacheKey);
2469
+ const at = lruOrder.lastIndexOf(cacheKey);
2470
+ if (at !== -1) lruOrder.splice(at, 1);
2471
+ }
2472
+ disposeOwned(node);
2473
+ }
2474
+ console.error("[KeepAliveRoute] Component error:", error);
2475
+ if (!kaTorn && seq === updateSeq) loaded.cancel();
2476
+ return;
2477
+ }
2478
+ if (kaTorn) return;
2479
+ if (seq === updateSeq) loaded.done();
2480
+ if (fromCache) activate(node);
2481
+ while (!kaTorn && lruOrder.length > maxCache) {
2133
2482
  const evictKey = lruOrder.shift();
2134
2483
  if (evictKey === cacheKey) {
2135
2484
  lruOrder.push(evictKey);
2136
2485
  break;
2137
2486
  }
2138
2487
  const evictNode = cache.get(evictKey);
2488
+ cache.delete(evictKey);
2139
2489
  if (evictNode) {
2140
- disposeOwned(evictNode);
2141
- if (evictNode.parentNode) evictNode.parentNode.removeChild(evictNode);
2142
- cache.delete(evictKey);
2490
+ disposeCached(evictNode);
2491
+ evictNode.parentNode?.removeChild(evictNode);
2143
2492
  }
2144
2493
  }
2145
2494
  }
2146
- currentNode = node;
2147
- currentCached = fromCache || shouldCache;
2148
- currentKey = cacheKey;
2149
- parent.insertBefore(node, anchor.nextSibling);
2150
- };
2495
+ });
2151
2496
  let initialized = false;
2152
2497
  const wrappedUpdate = async () => {
2153
2498
  await update();
2154
2499
  initialized = true;
2155
2500
  };
2156
- const attachWait = attachRetry(anchor, update);
2157
- const kaTeardown = track(wrappedUpdate);
2501
+ const attachWait = attachRetry(anchor, () => whenActive(gate, update));
2502
+ const kaTeardown = reactiveBinding(wrappedUpdate, anchor);
2158
2503
  if (!initialized) {
2159
2504
  queueMicrotask(
2160
2505
  bindOwnerScope(() => {
2161
- if (!initialized && anchor.parentNode) wrappedUpdate();
2506
+ whenActive(
2507
+ gate,
2508
+ bindOwnerScope(() => {
2509
+ if (!initialized && anchor.parentNode) wrappedUpdate();
2510
+ })
2511
+ );
2162
2512
  })
2163
2513
  );
2164
2514
  }
2165
2515
  const kaCleanup = () => {
2166
2516
  if (kaTorn) return;
2167
2517
  kaTorn = true;
2518
+ gate.close();
2519
+ loaded.cancel();
2168
2520
  updateSeq++;
2169
2521
  kaTeardown();
2170
2522
  attachWait.cancel();
2171
2523
  for (const node of cache.values()) {
2172
- disposeOwned(node);
2524
+ disposeCached(node);
2173
2525
  if (node.parentNode) node.parentNode.removeChild(node);
2174
2526
  }
2175
2527
  cache.clear();
2528
+ suspended.clear();
2176
2529
  lruOrder.length = 0;
2177
2530
  if (currentNode?.parentNode) currentNode.parentNode.removeChild(currentNode);
2178
2531
  if (currentNode && !currentCached) disposeOwned(currentNode);
@@ -2180,6 +2533,7 @@ function KeepAliveRoute(options) {
2180
2533
  currentKey = "";
2181
2534
  currentCached = false;
2182
2535
  };
2536
+ registerNodesRange(anchor, () => [currentNode]);
2183
2537
  registerDisposer(anchor, kaCleanup);
2184
2538
  routeCleanups.push(kaCleanup);
2185
2539
  return anchor;
@@ -2255,6 +2609,15 @@ function Suspense(props) {
2255
2609
  const anchor = document.createComment("suspense-boundary");
2256
2610
  let currentNode = null;
2257
2611
  let fallbackNode = null;
2612
+ const frozen = captureFrozen();
2613
+ let contentOwner = structuralBinding(NOOP);
2614
+ let fallbackOwner = null;
2615
+ const endContent = () => {
2616
+ const owner = contentOwner;
2617
+ contentOwner = null;
2618
+ owner?.stop();
2619
+ };
2620
+ const loaded = beginLoading(anchor);
2258
2621
  let isLoading = false;
2259
2622
  let generation = 0;
2260
2623
  let tornDown = false;
@@ -2269,21 +2632,36 @@ function Suspense(props) {
2269
2632
  release(fallbackNode);
2270
2633
  currentNode = null;
2271
2634
  fallbackNode = null;
2635
+ endFallback();
2636
+ };
2637
+ const endFallback = () => {
2638
+ const owner = fallbackOwner;
2639
+ fallbackOwner = null;
2640
+ owner?.stop();
2272
2641
  };
2273
2642
  const showFallback = (myGeneration) => {
2274
2643
  if (fallbackNode || !props.fallback || tornDown) return;
2275
2644
  if (!anchor.parentNode) return;
2276
2645
  let fallback;
2277
2646
  try {
2278
- fallback = typeof props.fallback === "function" ? props.fallback() : props.fallback;
2647
+ const factory = props.fallback;
2648
+ if (typeof factory === "function") {
2649
+ const owner = fallbackOwner = frozen.run(() => structuralBinding(NOOP));
2650
+ fallback = owner.run(factory);
2651
+ } else fallback = factory;
2279
2652
  } catch (error) {
2280
2653
  console.error("[Suspense] Fallback error:", error);
2654
+ endFallback();
2655
+ return;
2656
+ }
2657
+ if (!fallback || fallback.nodeType !== 1) {
2658
+ endFallback();
2281
2659
  return;
2282
2660
  }
2283
- if (!fallback || fallback.nodeType !== 1) return;
2284
2661
  const parent = commitTarget(myGeneration);
2285
2662
  if (!parent) {
2286
2663
  release(fallback);
2664
+ endFallback();
2287
2665
  return;
2288
2666
  }
2289
2667
  fallbackNode = fallback;
@@ -2294,7 +2672,9 @@ function Suspense(props) {
2294
2672
  isLoading = true;
2295
2673
  const myGeneration = ++generation;
2296
2674
  try {
2297
- const result = props.nodes();
2675
+ const owner = contentOwner;
2676
+ if (owner === null) return;
2677
+ const result = owner.run(props.nodes);
2298
2678
  let element;
2299
2679
  const isNode = typeof result?.nodeType === "number";
2300
2680
  if (!isNode && result != null && typeof result.then === "function") {
@@ -2303,35 +2683,84 @@ function Suspense(props) {
2303
2683
  } else {
2304
2684
  element = result;
2305
2685
  }
2306
- const parent = commitTarget(myGeneration);
2307
- if (!parent) {
2308
- release(element);
2309
- cleanupNodes();
2310
- return;
2311
- }
2312
- cleanupNodes();
2313
- parent.insertBefore(element, anchor.nextSibling);
2314
- currentNode = element;
2686
+ settle(myGeneration, true, element);
2315
2687
  } catch (error) {
2316
2688
  console.error("[Suspense] Nodes error:", error);
2317
- const parent = commitTarget(myGeneration);
2318
- cleanupNodes();
2319
- if (!parent) return;
2320
- const errorElement = document.createElement("div");
2321
- errorElement.className = "suspense-error";
2322
- errorElement.textContent = error instanceof Error ? error.message : "Failed to load";
2323
- parent.insertBefore(errorElement, anchor.nextSibling);
2324
- currentNode = errorElement;
2689
+ endContent();
2690
+ settle(myGeneration, false, error);
2325
2691
  } finally {
2326
2692
  isLoading = false;
2327
2693
  }
2328
2694
  };
2695
+ const committing = { cancel: NOOP };
2696
+ let dropWait = null;
2697
+ const settle = (myGeneration, ok, value) => {
2698
+ let view;
2699
+ if (ok) view = value;
2700
+ else {
2701
+ view = document.createElement("div");
2702
+ view.className = "suspense-error";
2703
+ view.textContent = value instanceof Error ? value.message : "Failed to load";
2704
+ }
2705
+ commitWhenActive(committing, {
2706
+ held: frozen,
2707
+ node: view,
2708
+ owned: () => !tornDown && myGeneration === generation,
2709
+ // Never shown: the content (or what is left of it) is abandoned.
2710
+ discard: () => {
2711
+ release(view);
2712
+ endContent();
2713
+ loaded.cancel();
2714
+ },
2715
+ teardown: () => {
2716
+ if (!anchor.parentNode || !(currentNode || fallbackNode || fallbackOwner)) return false;
2717
+ cleanupNodes();
2718
+ return true;
2719
+ },
2720
+ insert: () => {
2721
+ const parent = commitTarget(myGeneration);
2722
+ if (!parent) {
2723
+ release(view);
2724
+ cleanupNodes();
2725
+ endContent();
2726
+ loaded.cancel();
2727
+ return;
2728
+ }
2729
+ currentNode = view;
2730
+ try {
2731
+ parent.insertBefore(view, anchor.nextSibling);
2732
+ } catch (error) {
2733
+ if (currentNode === view) currentNode = null;
2734
+ release(view);
2735
+ endContent();
2736
+ console.error("[Suspense] Nodes error:", error);
2737
+ if (ok) settle(myGeneration, false, error);
2738
+ else loaded.cancel();
2739
+ return;
2740
+ }
2741
+ if (!tornDown && myGeneration === generation) loaded.done();
2742
+ }
2743
+ });
2744
+ };
2745
+ registerNodesRange(anchor, () => [currentNode, fallbackNode]);
2329
2746
  registerDisposer(anchor, () => {
2330
2747
  tornDown = true;
2331
2748
  generation++;
2749
+ dropWait?.();
2750
+ dropWait = null;
2751
+ committing.cancel();
2332
2752
  cleanupNodes();
2753
+ endContent();
2754
+ loaded.cancel();
2755
+ });
2756
+ const renderInScope = bindOwnerScope(() => {
2757
+ if (!tornDown) render();
2333
2758
  });
2334
- queueMicrotask(bindOwnerScope(render));
2759
+ queueMicrotask(
2760
+ bindOwnerScope(() => {
2761
+ dropWait = frozen.wait(renderInScope, NOOP);
2762
+ })
2763
+ );
2335
2764
  return anchor;
2336
2765
  }
2337
2766
  var LAZY_MARKER = /* @__PURE__ */ Symbol.for("sibujs:lazy");
@@ -2393,11 +2822,14 @@ function __removeRouterPagehideHandler() {
2393
2822
  function Outlet() {
2394
2823
  const anchor = document.createComment("route-outlet-nested");
2395
2824
  const adoptScope = captureScope();
2825
+ const frozen = captureFrozen();
2826
+ const gate = pauseGate(frozen);
2396
2827
  let currentNode = null;
2397
2828
  let currentChild = null;
2398
2829
  let currentKey = null;
2399
2830
  let navSeq = 0;
2400
2831
  let outletTorn = false;
2832
+ const loaded = beginLoading(anchor);
2401
2833
  const clearCurrent = () => {
2402
2834
  if (currentNode) {
2403
2835
  disposeOwned(currentNode);
@@ -2423,21 +2855,25 @@ function Outlet() {
2423
2855
  const route2 = scopedRoute(_routerRef.current);
2424
2856
  if (route2.matched.length < 2) {
2425
2857
  clearCurrent();
2858
+ settleEmpty(route2, loaded, () => !outletTorn && seq === navSeq);
2426
2859
  return;
2427
2860
  }
2428
2861
  const childRoute = route2.matched[route2.matched.length - 1];
2429
2862
  if (!childRoute || !("component" in childRoute)) {
2430
2863
  clearCurrent();
2864
+ loaded.done();
2431
2865
  return;
2432
2866
  }
2433
2867
  const key = instanceKey(childRoute, route2.matched.slice(1), route2);
2434
2868
  if (childRoute === currentChild && key === currentKey && currentNode) {
2435
2869
  updateInstanceRoute(currentNode, route2);
2870
+ loaded.done();
2436
2871
  return;
2437
2872
  }
2438
2873
  try {
2439
2874
  const cacheKey = `${route2.path}\0${childRoute.path}`;
2440
2875
  const plan = await _routerRef.current.loadPlan(childRoute, cacheKey);
2876
+ while (gate.held()) if (!await gate.until()) return;
2441
2877
  if (!commitTarget(seq)) {
2442
2878
  retryIfDetached(seq);
2443
2879
  return;
@@ -2448,31 +2884,64 @@ function Outlet() {
2448
2884
  childRoute,
2449
2885
  cacheKey,
2450
2886
  route2,
2451
- keyedScope(adoptScope, childRoute.path)
2887
+ keyedScope(adoptScope, childRoute.path),
2888
+ gate,
2889
+ () => commitTarget(seq) !== null
2452
2890
  );
2453
- const parent = commitTarget(seq);
2454
- if (!parent) {
2455
- release(node);
2456
- retryIfDetached(seq);
2457
- return;
2458
- }
2459
- if (!node) return;
2460
- clearCurrent();
2461
- parent.insertBefore(node, anchor.nextSibling);
2462
- currentNode = node;
2463
- currentChild = childRoute;
2464
- currentKey = key;
2891
+ await commitPass(gate, {
2892
+ node,
2893
+ owned: () => !outletTorn && seq === navSeq,
2894
+ discard: () => release(node),
2895
+ teardown: () => {
2896
+ if (!node || !anchor.parentNode || !currentNode) return false;
2897
+ clearCurrent();
2898
+ return true;
2899
+ },
2900
+ insert: () => {
2901
+ const parent = commitTarget(seq);
2902
+ if (!parent) {
2903
+ release(node);
2904
+ retryIfDetached(seq);
2905
+ return;
2906
+ }
2907
+ if (node) {
2908
+ currentNode = node;
2909
+ currentChild = childRoute;
2910
+ currentKey = key;
2911
+ try {
2912
+ parent.insertBefore(node, anchor.nextSibling);
2913
+ } catch (error) {
2914
+ if (currentNode === node) {
2915
+ currentNode = null;
2916
+ currentChild = null;
2917
+ currentKey = null;
2918
+ }
2919
+ release(node);
2920
+ throw error;
2921
+ }
2922
+ }
2923
+ if (!outletTorn && seq === navSeq) loaded.done();
2924
+ }
2925
+ });
2465
2926
  } catch (error) {
2466
- if (outletTorn || seq !== navSeq) return;
2927
+ if (cancellations.has(error) || outletTorn || seq !== navSeq) return;
2467
2928
  console.error("[Outlet] Failed to render child route:", error);
2929
+ loaded.cancel();
2468
2930
  }
2469
2931
  };
2470
- const attachWait = attachRetry(anchor, update);
2471
- const outletTeardown = track(update);
2932
+ const attachWait = attachRetry(anchor, () => whenActive(gate, update));
2933
+ const outletTeardown = reactiveBinding(() => {
2934
+ void update();
2935
+ }, anchor);
2472
2936
  if (!anchor.parentNode) {
2473
2937
  queueMicrotask(
2474
2938
  bindOwnerScope(() => {
2475
- if (anchor.parentNode) update();
2939
+ whenActive(
2940
+ gate,
2941
+ bindOwnerScope(() => {
2942
+ if (anchor.parentNode) update();
2943
+ })
2944
+ );
2476
2945
  })
2477
2946
  );
2478
2947
  }
@@ -2480,6 +2949,8 @@ function Outlet() {
2480
2949
  if (outletTorn) return;
2481
2950
  outletTorn = true;
2482
2951
  navSeq++;
2952
+ gate.close();
2953
+ loaded.cancel();
2483
2954
  outletTeardown();
2484
2955
  attachWait.cancel();
2485
2956
  if (currentNode) {
@@ -2490,6 +2961,7 @@ function Outlet() {
2490
2961
  currentChild = null;
2491
2962
  currentKey = null;
2492
2963
  };
2964
+ registerNodesRange(anchor, () => [currentNode]);
2493
2965
  registerDisposer(anchor, outletCleanup);
2494
2966
  routeCleanups.push(outletCleanup);
2495
2967
  return anchor;