@fictjs/router 0.4.0 → 0.5.1

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.cjs CHANGED
@@ -100,11 +100,30 @@ __export(index_exports, {
100
100
  module.exports = __toCommonJS(index_exports);
101
101
 
102
102
  // src/components.tsx
103
- var import_runtime2 = require("@fictjs/runtime");
104
- var import_advanced = require("@fictjs/runtime/advanced");
103
+ var import_runtime3 = require("@fictjs/runtime");
104
+ var import_advanced3 = require("@fictjs/runtime/advanced");
105
+
106
+ // src/accessor-utils.ts
107
+ function wrapAccessor(fn) {
108
+ const wrapped = ((...args) => {
109
+ if (args.length === 0) return wrapped;
110
+ return fn(...args);
111
+ });
112
+ return wrapped;
113
+ }
114
+ function wrapValue(value) {
115
+ const wrapped = (() => value);
116
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
117
+ const primitive = value;
118
+ wrapped.toString = () => String(primitive);
119
+ wrapped.valueOf = () => primitive;
120
+ }
121
+ return wrapped;
122
+ }
105
123
 
106
124
  // src/context.ts
107
125
  var import_runtime = require("@fictjs/runtime");
126
+ var import_advanced = require("@fictjs/runtime/advanced");
108
127
 
109
128
  // src/utils.ts
110
129
  function normalizePath(path) {
@@ -433,23 +452,76 @@ function isBrowser() {
433
452
  }
434
453
 
435
454
  // src/context.ts
436
- var defaultRouterContext = {
437
- location: () => ({
438
- pathname: "/",
439
- search: "",
440
- hash: "",
441
- state: null,
442
- key: "default"
443
- }),
444
- params: () => ({}),
445
- matches: () => [],
446
- navigate: () => {
455
+ function readAccessor(value) {
456
+ return typeof value === "function" ? value() : value;
457
+ }
458
+ var activeRouter = (0, import_advanced.createSignal)(null);
459
+ var activeRouterStack = [];
460
+ function pushActiveRouter(router) {
461
+ activeRouterStack.push(router);
462
+ (0, import_runtime.batch)(() => {
463
+ activeRouter(router);
464
+ });
465
+ }
466
+ function popActiveRouter(router) {
467
+ const index = activeRouterStack.lastIndexOf(router);
468
+ if (index >= 0) {
469
+ activeRouterStack.splice(index, 1);
470
+ }
471
+ (0, import_runtime.batch)(() => {
472
+ activeRouter(activeRouterStack[activeRouterStack.length - 1] ?? null);
473
+ });
474
+ }
475
+ var defaultLocation = {
476
+ pathname: "/",
477
+ search: "",
478
+ hash: "",
479
+ state: null,
480
+ key: "default"
481
+ };
482
+ var defaultNavigate = wrapAccessor(((toOrDelta) => {
483
+ const router = activeRouter();
484
+ if (!router) {
447
485
  console.warn("[fict-router] No router found. Wrap your app in a <Router>");
486
+ return;
487
+ }
488
+ const navigate = readAccessor(router.navigate);
489
+ if (typeof toOrDelta === "number") {
490
+ return navigate(toOrDelta);
491
+ }
492
+ return navigate(toOrDelta);
493
+ }));
494
+ var defaultResolvePath = wrapAccessor((to) => {
495
+ const router = activeRouter();
496
+ if (router) {
497
+ return readAccessor(router.resolvePath)(to);
498
+ }
499
+ return typeof to === "string" ? to : to.pathname || "/";
500
+ });
501
+ var defaultRouterContext = {
502
+ location: () => {
503
+ const router = activeRouter();
504
+ return router ? readAccessor(router.location) : defaultLocation;
505
+ },
506
+ params: () => {
507
+ const router = activeRouter();
508
+ return router ? readAccessor(router.params) : {};
509
+ },
510
+ matches: () => {
511
+ const router = activeRouter();
512
+ return router ? readAccessor(router.matches) : [];
513
+ },
514
+ navigate: defaultNavigate,
515
+ isRouting: () => {
516
+ const router = activeRouter();
517
+ return router ? readAccessor(router.isRouting) : false;
448
518
  },
449
- isRouting: () => false,
450
- pendingLocation: () => null,
451
- base: "",
452
- resolvePath: (to) => typeof to === "string" ? to : to.pathname || "/"
519
+ pendingLocation: () => {
520
+ const router = activeRouter();
521
+ return router ? readAccessor(router.pendingLocation) : null;
522
+ },
523
+ base: wrapValue(""),
524
+ resolvePath: defaultResolvePath
453
525
  };
454
526
  var RouterContext = (0, import_runtime.createContext)(defaultRouterContext);
455
527
  RouterContext.displayName = "RouterContext";
@@ -460,17 +532,50 @@ var defaultRouteContext = {
460
532
  match: () => void 0,
461
533
  data: () => void 0,
462
534
  outlet: () => null,
463
- resolvePath: (to) => typeof to === "string" ? to : to.pathname || "/"
535
+ resolvePath: wrapAccessor((to) => typeof to === "string" ? to : to.pathname || "/")
464
536
  };
465
537
  var RouteContext = (0, import_runtime.createContext)(defaultRouteContext);
466
538
  RouteContext.displayName = "RouteContext";
467
539
  function useRoute() {
468
540
  return (0, import_runtime.useContext)(RouteContext);
469
541
  }
542
+ var activeBeforeLeave = (0, import_advanced.createSignal)(null);
543
+ var activeBeforeLeaveStack = [];
544
+ function pushActiveBeforeLeave(context) {
545
+ activeBeforeLeaveStack.push(context);
546
+ (0, import_runtime.batch)(() => {
547
+ activeBeforeLeave(context);
548
+ });
549
+ }
550
+ function popActiveBeforeLeave(context) {
551
+ const index = activeBeforeLeaveStack.lastIndexOf(context);
552
+ if (index >= 0) {
553
+ activeBeforeLeaveStack.splice(index, 1);
554
+ }
555
+ (0, import_runtime.batch)(() => {
556
+ activeBeforeLeave(activeBeforeLeaveStack[activeBeforeLeaveStack.length - 1] ?? null);
557
+ });
558
+ }
470
559
  var defaultBeforeLeaveContext = {
471
- addHandler: () => () => {
472
- },
473
- confirm: async () => true
560
+ addHandler: wrapAccessor((handler) => {
561
+ const context = activeBeforeLeave();
562
+ if (context) {
563
+ return readAccessor(
564
+ context.addHandler
565
+ )(handler);
566
+ }
567
+ return () => {
568
+ };
569
+ }),
570
+ confirm: wrapAccessor((to, from) => {
571
+ const context = activeBeforeLeave();
572
+ if (context) {
573
+ return readAccessor(
574
+ context.confirm
575
+ )(to, from);
576
+ }
577
+ return Promise.resolve(true);
578
+ })
474
579
  };
475
580
  var BeforeLeaveContext = (0, import_runtime.createContext)(defaultBeforeLeaveContext);
476
581
  BeforeLeaveContext.displayName = "BeforeLeaveContext";
@@ -485,27 +590,28 @@ var RouteErrorContext = (0, import_runtime.createContext)(defaultRouteErrorConte
485
590
  RouteErrorContext.displayName = "RouteErrorContext";
486
591
  function useNavigate() {
487
592
  const router = useRouter();
488
- return router.navigate;
593
+ return readAccessor(router.navigate);
489
594
  }
490
595
  function useLocation() {
491
596
  const router = useRouter();
492
- return router.location;
597
+ return () => readAccessor(router.location);
493
598
  }
494
599
  function useParams() {
495
600
  const router = useRouter();
496
- return router.params;
601
+ return () => readAccessor(router.params);
497
602
  }
498
603
  function useSearchParams() {
499
604
  const router = useRouter();
500
605
  const getSearchParams = () => {
501
- const location = router.location();
606
+ const location = readAccessor(router.location);
502
607
  return new URLSearchParams(location.search);
503
608
  };
504
609
  const setSearchParams = (params, options) => {
505
610
  const searchParams = params instanceof URLSearchParams ? params : new URLSearchParams(params);
506
611
  const search = searchParams.toString();
507
- const location = router.location();
508
- router.navigate(
612
+ const location = readAccessor(router.location);
613
+ const navigate = readAccessor(router.navigate);
614
+ navigate(
509
615
  {
510
616
  pathname: location.pathname,
511
617
  search: search ? "?" + search : "",
@@ -514,23 +620,23 @@ function useSearchParams() {
514
620
  { replace: options?.replace }
515
621
  );
516
622
  };
517
- return [getSearchParams, setSearchParams];
623
+ return [getSearchParams, wrapAccessor(setSearchParams)];
518
624
  }
519
625
  function useMatches() {
520
626
  const router = useRouter();
521
- return router.matches;
627
+ return () => readAccessor(router.matches);
522
628
  }
523
629
  function useIsRouting() {
524
630
  const router = useRouter();
525
- return router.isRouting;
631
+ return () => readAccessor(router.isRouting);
526
632
  }
527
633
  function usePendingLocation() {
528
634
  const router = useRouter();
529
- return router.pendingLocation;
635
+ return () => readAccessor(router.pendingLocation);
530
636
  }
531
637
  function useRouteData() {
532
638
  const route = useRoute();
533
- return route.data;
639
+ return () => readAccessor(route.data);
534
640
  }
535
641
  function useRouteError() {
536
642
  const errorContext = (0, import_runtime.useContext)(RouteErrorContext);
@@ -538,20 +644,23 @@ function useRouteError() {
538
644
  return errorContext.error;
539
645
  }
540
646
  const route = useRoute();
541
- return route.error?.();
647
+ const routeError = route.error;
648
+ if (routeError === void 0) return void 0;
649
+ return readAccessor(routeError);
542
650
  }
543
651
  function useResolvedPath(to) {
544
652
  const route = useRoute();
545
653
  return () => {
546
654
  const target = typeof to === "function" ? to() : to;
547
- return route.resolvePath(target);
655
+ const resolvePath2 = readAccessor(route.resolvePath);
656
+ return resolvePath2(target);
548
657
  };
549
658
  }
550
659
  function useMatch(path) {
551
660
  const router = useRouter();
552
661
  return () => {
553
662
  const targetPath = typeof path === "function" ? path() : path;
554
- const matches = router.matches();
663
+ const matches = readAccessor(router.matches);
555
664
  for (const match of matches) {
556
665
  if (match.pattern === targetPath || match.pathname === targetPath) {
557
666
  return match;
@@ -564,6 +673,7 @@ function useHref(to) {
564
673
  const router = useRouter();
565
674
  return () => {
566
675
  const target = typeof to === "function" ? to() : to;
676
+ const base = readAccessor(router.base);
567
677
  let pathname;
568
678
  let search = "";
569
679
  let hash = "";
@@ -587,16 +697,17 @@ function useHref(to) {
587
697
  }
588
698
  let resolved;
589
699
  if (pathname === "") {
590
- const currentPathname = router.location().pathname;
591
- const normalizedBase = router.base === "/" || router.base === "" ? "" : router.base;
700
+ const currentPathname = readAccessor(router.location).pathname;
701
+ const normalizedBase = base === "/" || base === "" ? "" : base;
592
702
  if (normalizedBase && !currentPathname.startsWith(normalizedBase)) {
593
703
  return currentPathname + search + hash;
594
704
  }
595
- resolved = stripBasePath(currentPathname, router.base);
705
+ resolved = stripBasePath(currentPathname, base);
596
706
  } else {
597
- resolved = router.resolvePath(pathname);
707
+ const resolvePath2 = readAccessor(router.resolvePath);
708
+ resolved = resolvePath2(pathname);
598
709
  }
599
- const baseHref = prependBasePath(resolved, router.base);
710
+ const baseHref = prependBasePath(resolved, base);
600
711
  return baseHref + search + hash;
601
712
  };
602
713
  }
@@ -604,12 +715,14 @@ function useIsActive(to, options) {
604
715
  const router = useRouter();
605
716
  return () => {
606
717
  const target = typeof to === "function" ? to() : to;
607
- const resolvedTargetPath = router.resolvePath(target);
608
- const currentPath = router.location().pathname;
609
- if (router.base && currentPath !== router.base && !currentPath.startsWith(router.base + "/")) {
718
+ const resolvePath2 = readAccessor(router.resolvePath);
719
+ const resolvedTargetPath = resolvePath2(target);
720
+ const currentPath = readAccessor(router.location).pathname;
721
+ const base = readAccessor(router.base);
722
+ if (base && currentPath !== base && !currentPath.startsWith(base + "/")) {
610
723
  return false;
611
724
  }
612
- const currentPathWithoutBase = stripBasePath(currentPath, router.base);
725
+ const currentPathWithoutBase = stripBasePath(currentPath, base);
613
726
  if (options?.end) {
614
727
  return currentPathWithoutBase === resolvedTargetPath;
615
728
  }
@@ -1089,6 +1202,38 @@ function createStaticHistory(url) {
1089
1202
  };
1090
1203
  }
1091
1204
 
1205
+ // src/router-internals.ts
1206
+ var import_meta = {};
1207
+ var isDevEnv = typeof import_meta !== "undefined" && import_meta.env?.DEV === true || typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1208
+ var didWarnBaseMismatch = false;
1209
+ function hasBasePrefix(pathname, base) {
1210
+ if (!base) return true;
1211
+ return pathname === base || pathname.startsWith(base + "/");
1212
+ }
1213
+ function stripBaseOrWarn(pathname, base) {
1214
+ if (!base) return pathname;
1215
+ if (!hasBasePrefix(pathname, base)) {
1216
+ if (isDevEnv && !didWarnBaseMismatch) {
1217
+ didWarnBaseMismatch = true;
1218
+ console.warn(
1219
+ `[fict-router] Location "${pathname}" does not start with base "${base}". No routes matched.`
1220
+ );
1221
+ }
1222
+ return null;
1223
+ }
1224
+ return stripBasePath(pathname, base);
1225
+ }
1226
+ function stripBaseIfPresent(pathname, base) {
1227
+ if (!base) return pathname;
1228
+ if (!hasBasePrefix(pathname, base)) return pathname;
1229
+ return stripBasePath(pathname, base);
1230
+ }
1231
+
1232
+ // src/router-provider.ts
1233
+ var import_runtime2 = require("@fictjs/runtime");
1234
+ var import_advanced2 = require("@fictjs/runtime/advanced");
1235
+ var import_jsx_runtime = require("@fictjs/runtime/jsx-runtime");
1236
+
1092
1237
  // src/scroll.ts
1093
1238
  var scrollPositions = /* @__PURE__ */ new Map();
1094
1239
  var MAX_STORED_POSITIONS = 100;
@@ -1211,35 +1356,7 @@ function configureScrollRestoration(options) {
1211
1356
  defaultScrollRestoration = createScrollRestoration(options);
1212
1357
  }
1213
1358
 
1214
- // src/components.tsx
1215
- var import_jsx_runtime = require("fict/jsx-runtime");
1216
- var import_meta = {};
1217
- var isDevEnv = typeof import_meta !== "undefined" && import_meta.env?.DEV === true || typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1218
- var didWarnBaseMismatch = false;
1219
- function hasBasePrefix(pathname, base) {
1220
- if (!base) return true;
1221
- return pathname === base || pathname.startsWith(base + "/");
1222
- }
1223
- function stripBaseOrWarn(pathname, base) {
1224
- if (!base) return pathname;
1225
- if (!hasBasePrefix(pathname, base)) {
1226
- if (isDevEnv && !didWarnBaseMismatch) {
1227
- didWarnBaseMismatch = true;
1228
- console.warn(
1229
- `[fict-router] Location "${pathname}" does not start with base "${base}". No routes matched.`
1230
- );
1231
- }
1232
- return null;
1233
- }
1234
- return stripBasePath(pathname, base);
1235
- }
1236
- function stripBaseIfPresent(pathname, base) {
1237
- if (!base) return pathname;
1238
- if (hasBasePrefix(pathname, base)) {
1239
- return stripBasePath(pathname, base);
1240
- }
1241
- return pathname;
1242
- }
1359
+ // src/router-provider.ts
1243
1360
  function createRouterState(history2, routes, base = "") {
1244
1361
  const normalizedBase = normalizePath(base);
1245
1362
  const baseForStrip = normalizedBase === "/" ? "" : normalizedBase;
@@ -1252,10 +1369,10 @@ function createRouterState(history2, routes, base = "") {
1252
1369
  };
1253
1370
  const initialLocation = history2.location;
1254
1371
  const initialMatches = matchWithBase(initialLocation.pathname);
1255
- const locationSignal = (0, import_advanced.createSignal)(initialLocation);
1256
- const matchesSignal = (0, import_advanced.createSignal)(initialMatches);
1257
- const isRoutingSignal = (0, import_advanced.createSignal)(false);
1258
- const pendingLocationSignal = (0, import_advanced.createSignal)(null);
1372
+ const locationSignal = (0, import_advanced2.createSignal)(initialLocation);
1373
+ const matchesSignal = (0, import_advanced2.createSignal)(initialMatches);
1374
+ const isRoutingSignal = (0, import_advanced2.createSignal)(false);
1375
+ const pendingLocationSignal = (0, import_advanced2.createSignal)(null);
1259
1376
  const beforeLeaveHandlers = /* @__PURE__ */ new Set();
1260
1377
  let navigationToken = 0;
1261
1378
  const beforeLeave = {
@@ -1266,7 +1383,7 @@ function createRouterState(history2, routes, base = "") {
1266
1383
  async confirm(to, from) {
1267
1384
  if (beforeLeaveHandlers.size === 0) return true;
1268
1385
  const currentToken = ++navigationToken;
1269
- let defaultPrevented = false;
1386
+ let defaultPrevented = true;
1270
1387
  let retryRequested = false;
1271
1388
  let forceRetry = false;
1272
1389
  const event = {
@@ -1363,9 +1480,11 @@ function createRouterState(history2, routes, base = "") {
1363
1480
  }
1364
1481
  const targetLocation = createLocation(locationSpec, finalState, toKey);
1365
1482
  (0, import_runtime2.untrack)(async () => {
1483
+ if (beforeLeaveHandlers.size > 0) {
1484
+ pendingLocationSignal(targetLocation);
1485
+ }
1366
1486
  const canNavigate = await beforeLeave.confirm(targetLocation, currentLocation);
1367
1487
  if (!canNavigate) {
1368
- pendingLocationSignal(null);
1369
1488
  return;
1370
1489
  }
1371
1490
  (0, import_runtime2.batch)(() => {
@@ -1431,6 +1550,17 @@ function RouterProvider(props) {
1431
1550
  props.base
1432
1551
  );
1433
1552
  (0, import_runtime2.onCleanup)(cleanup);
1553
+ const beforeLeaveContext = {
1554
+ addHandler: wrapAccessor(beforeLeave.addHandler),
1555
+ confirm: wrapAccessor(beforeLeave.confirm)
1556
+ };
1557
+ const resolvePathFn = (to) => {
1558
+ const location = state().location;
1559
+ const currentPathWithoutBase = stripBaseOrWarn(location.pathname, normalizedBase) || "/";
1560
+ const rawTargetPath = typeof to === "string" ? to : to.pathname || "/";
1561
+ const targetPath = rawTargetPath.startsWith("/") ? stripBaseIfPresent(rawTargetPath, normalizedBase) : rawTargetPath;
1562
+ return resolvePath(currentPathWithoutBase, targetPath);
1563
+ };
1434
1564
  const routerContext = {
1435
1565
  location: () => state().location,
1436
1566
  params: () => {
@@ -1442,30 +1572,41 @@ function RouterProvider(props) {
1442
1572
  return allParams;
1443
1573
  },
1444
1574
  matches: () => state().matches,
1445
- navigate,
1575
+ navigate: wrapAccessor(navigate),
1446
1576
  isRouting: () => state().isRouting,
1447
1577
  pendingLocation: () => state().pendingLocation,
1448
- base: normalizedBase,
1449
- resolvePath: (to) => {
1450
- const location = state().location;
1451
- const currentPathWithoutBase = stripBaseOrWarn(location.pathname, normalizedBase) || "/";
1452
- const rawTargetPath = typeof to === "string" ? to : to.pathname || "/";
1453
- const targetPath = rawTargetPath.startsWith("/") ? stripBaseIfPresent(rawTargetPath, normalizedBase) : rawTargetPath;
1454
- return resolvePath(currentPathWithoutBase, targetPath);
1455
- }
1578
+ base: wrapValue(normalizedBase),
1579
+ resolvePath: wrapAccessor(resolvePathFn)
1456
1580
  };
1457
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterContext.Provider, { value: routerContext, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BeforeLeaveContext.Provider, { value: beforeLeave, children: props.children }) });
1581
+ pushActiveRouter(routerContext);
1582
+ pushActiveBeforeLeave(beforeLeaveContext);
1583
+ (0, import_runtime2.onCleanup)(() => {
1584
+ popActiveBeforeLeave(beforeLeaveContext);
1585
+ popActiveRouter(routerContext);
1586
+ });
1587
+ const RouterContextProvider = RouterContext.Provider;
1588
+ const BeforeLeaveProvider = BeforeLeaveContext.Provider;
1589
+ return (0, import_jsx_runtime.jsx)(RouterContextProvider, {
1590
+ value: routerContext,
1591
+ children: (0, import_jsx_runtime.jsx)(BeforeLeaveProvider, {
1592
+ value: beforeLeaveContext,
1593
+ children: props.children
1594
+ })
1595
+ });
1458
1596
  }
1597
+
1598
+ // src/components.tsx
1599
+ var import_jsx_runtime2 = require("fict/jsx-runtime");
1459
1600
  function Router(props) {
1460
1601
  const history2 = props.history || createBrowserHistory();
1461
1602
  const routes = extractRoutes(props.children);
1462
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Routes, { children: props.children }) });
1603
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Routes, { children: props.children }) });
1463
1604
  }
1464
1605
  function HashRouter(props) {
1465
1606
  const hashOptions = props.hashType ? { hashType: props.hashType } : void 0;
1466
1607
  const history2 = createHashHistory(hashOptions);
1467
1608
  const routes = extractRoutes(props.children);
1468
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Routes, { children: props.children }) });
1609
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Routes, { children: props.children }) });
1469
1610
  }
1470
1611
  function MemoryRouter(props) {
1471
1612
  const memoryOptions = {};
@@ -1479,12 +1620,12 @@ function MemoryRouter(props) {
1479
1620
  Object.keys(memoryOptions).length > 0 ? memoryOptions : void 0
1480
1621
  );
1481
1622
  const routes = extractRoutes(props.children);
1482
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Routes, { children: props.children }) });
1623
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Routes, { children: props.children }) });
1483
1624
  }
1484
1625
  function StaticRouter(props) {
1485
1626
  const history2 = createStaticHistory(props.url);
1486
1627
  const routes = extractRoutes(props.children);
1487
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Routes, { children: props.children }) });
1628
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterProvider, { history: history2, routes, base: props.base, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Routes, { children: props.children }) });
1488
1629
  }
1489
1630
  function Routes(props) {
1490
1631
  const router = useRouter();
@@ -1492,10 +1633,12 @@ function Routes(props) {
1492
1633
  const routes = extractRoutes(props.children);
1493
1634
  const compiledRoutes = routes.map((r) => compileRoute(r));
1494
1635
  const branches = createBranches(compiledRoutes);
1495
- const currentMatches = (0, import_runtime2.createMemo)(() => {
1496
- const location = router.location();
1497
- const parentMatch = parentRoute.match();
1498
- const locationPath = stripBaseOrWarn(location.pathname, router.base);
1636
+ const currentMatches = (0, import_runtime3.createMemo)(() => {
1637
+ const pendingLocation = readAccessor(router.pendingLocation);
1638
+ const location = pendingLocation ?? readAccessor(router.location);
1639
+ const parentMatch = readAccessor(parentRoute.match);
1640
+ const base = readAccessor(router.base);
1641
+ const locationPath = stripBaseOrWarn(location.pathname, base);
1499
1642
  if (locationPath == null) return [];
1500
1643
  let basePath = "/";
1501
1644
  if (parentMatch) {
@@ -1504,24 +1647,22 @@ function Routes(props) {
1504
1647
  const relativePath = locationPath.startsWith(basePath) ? locationPath.slice(basePath.length) || "/" : locationPath;
1505
1648
  return matchRoutes(branches, relativePath) || [];
1506
1649
  });
1507
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: renderMatches(currentMatches(), 0) });
1650
+ const matches = currentMatches();
1651
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: matches.length > 0 ? renderMatches(matches, 0) : null });
1508
1652
  }
1509
1653
  function renderMatches(matches, index) {
1510
- if (index >= matches.length) {
1511
- return null;
1512
- }
1513
1654
  const match = matches[index];
1514
1655
  const route = match.route;
1515
1656
  const router = useRouter();
1516
- const dataState = (0, import_advanced.createSignal)({
1657
+ const dataState = (0, import_advanced3.createSignal)({
1517
1658
  data: void 0,
1518
1659
  error: void 0,
1519
1660
  loading: !!route.preload
1520
1661
  });
1521
1662
  let preloadToken = 0;
1522
1663
  if (route.preload) {
1523
- (0, import_runtime2.createEffect)(() => {
1524
- const location = router.location();
1664
+ (0, import_runtime3.createEffect)(() => {
1665
+ const location = readAccessor(router.location);
1525
1666
  const preloadArgs = {
1526
1667
  params: match.params,
1527
1668
  location,
@@ -1540,17 +1681,7 @@ function renderMatches(matches, index) {
1540
1681
  });
1541
1682
  });
1542
1683
  }
1543
- const routeContext = {
1544
- match: () => match,
1545
- data: () => dataState().data,
1546
- error: () => dataState().error,
1547
- outlet: () => renderMatches(matches, index + 1),
1548
- resolvePath: (to) => {
1549
- const basePath = match.pathname;
1550
- const targetPath = typeof to === "string" ? to : to.pathname || "/";
1551
- return resolvePath(basePath, targetPath);
1552
- }
1553
- };
1684
+ const outletNode = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Outlet, {});
1554
1685
  const renderContent = () => {
1555
1686
  const state = dataState();
1556
1687
  if (state.error !== void 0 && route.errorElement) {
@@ -1561,26 +1692,39 @@ function renderMatches(matches, index) {
1561
1692
  }
1562
1693
  if (route.component) {
1563
1694
  const Component = route.component;
1564
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, { params: match.params, location: router.location(), data: state.data, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {}) });
1565
- } else if (route.element) {
1695
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Component, { params: match.params, location: readAccessor(router.location), data: state.data, children: outletNode });
1696
+ }
1697
+ if (route.element) {
1566
1698
  return route.element;
1567
- } else if (route.children) {
1568
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {});
1699
+ }
1700
+ if (route.children) {
1701
+ return outletNode;
1569
1702
  }
1570
1703
  return null;
1571
1704
  };
1572
- let content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouteContext.Provider, { value: routeContext, children: renderContent() });
1705
+ const routeContext = {
1706
+ match: () => match,
1707
+ data: () => dataState().data,
1708
+ error: () => dataState().error,
1709
+ outlet: () => index + 1 < matches.length ? renderMatches(matches, index + 1) : null,
1710
+ resolvePath: wrapAccessor((to) => {
1711
+ const basePath = match.pathname;
1712
+ const targetPath = typeof to === "string" ? to : to.pathname || "/";
1713
+ return resolvePath(basePath, targetPath);
1714
+ })
1715
+ };
1716
+ let content = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouteContext.Provider, { value: routeContext, children: renderContent() });
1573
1717
  if (route.errorElement) {
1574
- content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1575
- import_runtime2.ErrorBoundary,
1718
+ content = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1719
+ import_runtime3.ErrorBoundary,
1576
1720
  {
1577
- fallback: (err, reset) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouteErrorContext.Provider, { value: { error: err, reset }, children: route.errorElement }),
1721
+ fallback: (err, reset) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouteErrorContext.Provider, { value: { error: err, reset }, children: route.errorElement }),
1578
1722
  children: content
1579
1723
  }
1580
1724
  );
1581
1725
  }
1582
1726
  if (route.loadingElement) {
1583
- content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_runtime2.Suspense, { fallback: route.loadingElement, children: content });
1727
+ content = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_runtime3.Suspense, { fallback: route.loadingElement, children: content });
1584
1728
  }
1585
1729
  return content;
1586
1730
  }
@@ -1589,11 +1733,11 @@ function Route(_props) {
1589
1733
  }
1590
1734
  function Outlet() {
1591
1735
  const route = useRoute();
1592
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: route.outlet() });
1736
+ return readAccessor(route.outlet);
1593
1737
  }
1594
1738
  function Navigate(props) {
1595
1739
  const router = useRouter();
1596
- (0, import_runtime2.createEffect)(() => {
1740
+ (0, import_runtime3.createEffect)(() => {
1597
1741
  router.navigate(props.to, {
1598
1742
  replace: props.replace ?? true,
1599
1743
  state: props.state
@@ -1603,7 +1747,7 @@ function Navigate(props) {
1603
1747
  }
1604
1748
  function Redirect(props) {
1605
1749
  const router = useRouter();
1606
- (0, import_runtime2.createEffect)(() => {
1750
+ (0, import_runtime3.createEffect)(() => {
1607
1751
  router.navigate(props.to, {
1608
1752
  replace: props.push !== true,
1609
1753
  // Replace by default, push only if explicitly requested
@@ -1633,7 +1777,7 @@ function extractRoutes(children) {
1633
1777
  routeDef.loadingElement = props.loadingElement;
1634
1778
  if (props.children) routeDef.children = extractRoutes(props.children);
1635
1779
  routes.push(routeDef);
1636
- } else if (vnode.type === import_runtime2.Fragment && vnode.props?.children) {
1780
+ } else if (vnode.type === import_runtime3.Fragment && vnode.props?.children) {
1637
1781
  routes.push(...extractRoutes(vnode.props.children));
1638
1782
  }
1639
1783
  }
@@ -1646,28 +1790,42 @@ function createRouter(routes, options) {
1646
1790
  return {
1647
1791
  Router: (props) => {
1648
1792
  const history2 = options?.history || createBrowserHistory();
1649
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, { history: history2, routes, base: options?.base, children: props.children || /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Routes, { children: routesToElements(routes) }) });
1793
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RouterProvider, { history: history2, routes, base: options?.base, children: props.children || /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Routes, { children: routesToElements(routes) }) });
1650
1794
  }
1651
1795
  };
1652
1796
  }
1653
1797
  function routesToElements(routes) {
1654
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: routes.map((route, i) => {
1798
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: routes.map((route, i) => {
1655
1799
  const routeProps = { key: route.key || `route-${i}` };
1656
1800
  if (route.path !== void 0) routeProps.path = route.path;
1657
1801
  if (route.component !== void 0) routeProps.component = route.component;
1658
1802
  if (route.element !== void 0) routeProps.element = route.element;
1659
1803
  if (route.index !== void 0) routeProps.index = route.index;
1660
1804
  if (route.children) routeProps.children = routesToElements(route.children);
1661
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Route, { ...routeProps });
1805
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Route, { ...routeProps });
1662
1806
  }) });
1663
1807
  }
1664
1808
 
1665
1809
  // src/link.tsx
1666
- var import_runtime3 = require("@fictjs/runtime");
1667
- var import_jsx_runtime2 = require("fict/jsx-runtime");
1810
+ var import_runtime4 = require("@fictjs/runtime");
1811
+ var import_internal = require("@fictjs/runtime/internal");
1812
+ var import_jsx_runtime3 = require("fict/jsx-runtime");
1813
+ var createSpreadRef = (props) => {
1814
+ let current = null;
1815
+ return (el) => {
1816
+ if (!el) {
1817
+ current = null;
1818
+ return;
1819
+ }
1820
+ if (el === current) return;
1821
+ current = el;
1822
+ (0, import_internal.spread)(el, props, false, true);
1823
+ };
1824
+ };
1668
1825
  function Link(props) {
1669
1826
  const router = useRouter();
1670
1827
  const href = useHref(() => props.to);
1828
+ const getHrefValue = () => readAccessor(readAccessor(href));
1671
1829
  let preloadTriggered = false;
1672
1830
  const handleClick = (event) => {
1673
1831
  if (props.onClick) {
@@ -1692,7 +1850,7 @@ function Link(props) {
1692
1850
  const triggerPreload = () => {
1693
1851
  if (preloadTriggered || props.disabled || props.prefetch === "none") return;
1694
1852
  preloadTriggered = true;
1695
- const hrefValue = href();
1853
+ const hrefValue = getHrefValue();
1696
1854
  if (typeof window !== "undefined" && window.dispatchEvent) {
1697
1855
  window.dispatchEvent(
1698
1856
  new CustomEvent("fict-router:preload", {
@@ -1705,14 +1863,16 @@ function Link(props) {
1705
1863
  if (props.prefetch === "intent" || props.prefetch === void 0) {
1706
1864
  triggerPreload();
1707
1865
  }
1708
- const onMouseEnter = props.onMouseEnter;
1866
+ const propsRecord = props;
1867
+ const onMouseEnter = propsRecord.onMouseEnter;
1709
1868
  if (onMouseEnter) onMouseEnter(event);
1710
1869
  };
1711
1870
  const handleFocus = (event) => {
1712
1871
  if (props.prefetch === "intent" || props.prefetch === void 0) {
1713
1872
  triggerPreload();
1714
1873
  }
1715
- const onFocus = props.onFocus;
1874
+ const propsRecord = props;
1875
+ const onFocus = propsRecord.onFocus;
1716
1876
  if (onFocus) onFocus(event);
1717
1877
  };
1718
1878
  const {
@@ -1725,20 +1885,24 @@ function Link(props) {
1725
1885
  prefetch,
1726
1886
  disabled,
1727
1887
  onClick: _onClick,
1888
+ onMouseEnter: _onMouseEnter,
1889
+ onFocus: _onFocus,
1728
1890
  children,
1729
1891
  ...anchorProps
1730
1892
  } = props;
1893
+ const anchorRef = createSpreadRef(anchorProps);
1894
+ const spanRef = createSpreadRef(anchorProps);
1731
1895
  if (disabled) {
1732
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { ...anchorProps, children });
1896
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { ref: spanRef, children });
1733
1897
  }
1734
1898
  if (prefetch === "render") {
1735
1899
  triggerPreload();
1736
1900
  }
1737
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1901
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1738
1902
  "a",
1739
1903
  {
1740
- ...anchorProps,
1741
- href: href(),
1904
+ ref: anchorRef,
1905
+ href: getHrefValue(),
1742
1906
  onClick: handleClick,
1743
1907
  onMouseEnter: handleMouseEnter,
1744
1908
  onFocus: handleFocus,
@@ -1750,12 +1914,14 @@ function NavLink(props) {
1750
1914
  const router = useRouter();
1751
1915
  const isActive = useIsActive(() => props.to, { end: props.end });
1752
1916
  const href = useHref(() => props.to);
1917
+ const getHrefValue = () => readAccessor(readAccessor(href));
1753
1918
  const pendingLocation = usePendingLocation();
1754
1919
  const computeIsPending = () => {
1755
1920
  const pending = pendingLocation();
1756
1921
  if (!pending) return false;
1757
- const resolvedHref = href();
1758
- const baseToStrip = router.base === "/" ? "" : router.base;
1922
+ const resolvedHref = getHrefValue();
1923
+ const base = readAccessor(router.base);
1924
+ const baseToStrip = base === "/" ? "" : base;
1759
1925
  const pendingPathWithoutBase = stripBasePath(pending.pathname, baseToStrip);
1760
1926
  const parsed = parseURL(resolvedHref);
1761
1927
  const targetPathWithoutBase = stripBasePath(parsed.pathname, baseToStrip);
@@ -1767,9 +1933,9 @@ function NavLink(props) {
1767
1933
  const getRenderProps = () => ({
1768
1934
  isActive: isActive(),
1769
1935
  isPending: computeIsPending(),
1770
- isTransitioning: router.isRouting()
1936
+ isTransitioning: readAccessor(router.isRouting)
1771
1937
  });
1772
- const computedClassName = (0, import_runtime3.createMemo)(() => {
1938
+ const computedClassName = (0, import_runtime4.createMemo)(() => {
1773
1939
  const renderProps = getRenderProps();
1774
1940
  const classes = [];
1775
1941
  if (typeof props.className === "function") {
@@ -1786,7 +1952,7 @@ function NavLink(props) {
1786
1952
  }
1787
1953
  return classes.join(" ") || void 0;
1788
1954
  });
1789
- const computedStyle = (0, import_runtime3.createMemo)(() => {
1955
+ const computedStyle = (0, import_runtime4.createMemo)(() => {
1790
1956
  const renderProps = getRenderProps();
1791
1957
  const style = {};
1792
1958
  if (typeof props.style === "function") {
@@ -1803,14 +1969,14 @@ function NavLink(props) {
1803
1969
  }
1804
1970
  return Object.keys(style).length > 0 ? style : void 0;
1805
1971
  });
1806
- const computedChildren = (0, import_runtime3.createMemo)(() => {
1972
+ const computedChildren = (0, import_runtime4.createMemo)(() => {
1807
1973
  const renderProps = getRenderProps();
1808
1974
  if (typeof props.children === "function") {
1809
1975
  return props.children(renderProps);
1810
1976
  }
1811
1977
  return props.children;
1812
1978
  });
1813
- const ariaCurrent = (0, import_runtime3.createMemo)(() => {
1979
+ const ariaCurrent = (0, import_runtime4.createMemo)(() => {
1814
1980
  const renderProps = getRenderProps();
1815
1981
  if (!renderProps.isActive) return void 0;
1816
1982
  return props["aria-current"] || "page";
@@ -1856,18 +2022,30 @@ function NavLink(props) {
1856
2022
  "aria-current": _ariaCurrent,
1857
2023
  ...anchorProps
1858
2024
  } = props;
2025
+ const anchorRef = createSpreadRef(anchorProps);
2026
+ const spanRef = createSpreadRef(anchorProps);
1859
2027
  if (disabled) {
1860
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { ...anchorProps, className: computedClassName(), style: computedStyle(), children: computedChildren() });
2028
+ const disabledClassName = computedClassName();
2029
+ const disabledStyle = computedStyle();
2030
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2031
+ "span",
2032
+ {
2033
+ ref: spanRef,
2034
+ ...disabledClassName !== void 0 ? { class: disabledClassName } : {},
2035
+ ...disabledStyle !== void 0 ? { style: disabledStyle } : {},
2036
+ children: computedChildren()
2037
+ }
2038
+ );
1861
2039
  }
1862
2040
  const finalClassName = computedClassName();
1863
2041
  const finalStyle = computedStyle();
1864
2042
  const finalAriaCurrent = ariaCurrent();
1865
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2043
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1866
2044
  "a",
1867
2045
  {
1868
- ...anchorProps,
1869
- href: href(),
1870
- ...finalClassName !== void 0 ? { className: finalClassName } : {},
2046
+ ref: anchorRef,
2047
+ href: getHrefValue(),
2048
+ ...finalClassName !== void 0 ? { class: finalClassName } : {},
1871
2049
  ...finalStyle !== void 0 ? { style: finalStyle } : {},
1872
2050
  ...finalAriaCurrent !== void 0 ? { "aria-current": finalAriaCurrent } : {},
1873
2051
  onClick: handleClick,
@@ -1888,7 +2066,7 @@ function Form(props) {
1888
2066
  event.preventDefault();
1889
2067
  const formData = new FormData(form);
1890
2068
  const method2 = props.method?.toUpperCase() || "GET";
1891
- const actionUrl = props.action || router.location().pathname;
2069
+ const actionUrl = props.action || readAccessor(router.location).pathname;
1892
2070
  if (method2 === "GET") {
1893
2071
  const searchParams = new URLSearchParams();
1894
2072
  formData.forEach((value, key) => {
@@ -1963,11 +2141,12 @@ function Form(props) {
1963
2141
  onSubmit: _onSubmit,
1964
2142
  ...formProps
1965
2143
  } = props;
2144
+ const formRef = createSpreadRef(formProps);
1966
2145
  const htmlMethod = method && ["get", "post"].includes(method) ? method : void 0;
1967
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2146
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1968
2147
  "form",
1969
2148
  {
1970
- ...formProps,
2149
+ ref: formRef,
1971
2150
  ...action2 !== void 0 ? { action: action2 } : {},
1972
2151
  ...htmlMethod !== void 0 ? { method: htmlMethod } : {},
1973
2152
  onSubmit: handleSubmit,
@@ -1977,8 +2156,8 @@ function Form(props) {
1977
2156
  }
1978
2157
 
1979
2158
  // src/data.ts
1980
- var import_runtime4 = require("@fictjs/runtime");
1981
- var import_advanced2 = require("@fictjs/runtime/advanced");
2159
+ var import_runtime5 = require("@fictjs/runtime");
2160
+ var import_advanced4 = require("@fictjs/runtime/advanced");
1982
2161
  var CACHE_DURATION = 3 * 60 * 1e3;
1983
2162
  var PRELOAD_CACHE_DURATION = 5 * 1e3;
1984
2163
  var MAX_CACHE_SIZE = 500;
@@ -2031,9 +2210,9 @@ function query(fn, name) {
2031
2210
  return () => cached.result;
2032
2211
  }
2033
2212
  }
2034
- const resultSignal = (0, import_advanced2.createSignal)(cached?.result);
2035
- const errorSignal = (0, import_advanced2.createSignal)(void 0);
2036
- const loadingSignal = (0, import_advanced2.createSignal)(true);
2213
+ const resultSignal = (0, import_advanced4.createSignal)(cached?.result);
2214
+ const errorSignal = (0, import_advanced4.createSignal)(void 0);
2215
+ const loadingSignal = (0, import_advanced4.createSignal)(true);
2037
2216
  const promise = Promise.resolve(fn(...args)).then((result) => {
2038
2217
  const entry = {
2039
2218
  timestamp: Date.now(),
@@ -2043,13 +2222,13 @@ function query(fn, name) {
2043
2222
  };
2044
2223
  queryCache.set(cacheKey, entry);
2045
2224
  evictOldestEntries();
2046
- (0, import_runtime4.batch)(() => {
2225
+ (0, import_runtime5.batch)(() => {
2047
2226
  resultSignal(result);
2048
2227
  loadingSignal(false);
2049
2228
  });
2050
2229
  return result;
2051
2230
  }).catch((error) => {
2052
- (0, import_runtime4.batch)(() => {
2231
+ (0, import_runtime5.batch)(() => {
2053
2232
  errorSignal(error);
2054
2233
  loadingSignal(false);
2055
2234
  });
@@ -2118,7 +2297,7 @@ function action(fn, name) {
2118
2297
  function getAction(url) {
2119
2298
  return actionRegistry.get(url);
2120
2299
  }
2121
- var activeSubmissions = (0, import_advanced2.createSignal)(/* @__PURE__ */ new Map());
2300
+ var activeSubmissions = (0, import_advanced4.createSignal)(/* @__PURE__ */ new Map());
2122
2301
  function useSubmission(actionOrUrl) {
2123
2302
  const url = typeof actionOrUrl === "string" ? actionOrUrl : actionOrUrl.url;
2124
2303
  return () => {
@@ -2173,10 +2352,10 @@ function createPreload(fn) {
2173
2352
  };
2174
2353
  }
2175
2354
  function createResource(source, fetcher) {
2176
- const dataSignal = (0, import_advanced2.createSignal)(void 0);
2177
- const loadingSignal = (0, import_advanced2.createSignal)(true);
2178
- const errorSignal = (0, import_advanced2.createSignal)(void 0);
2179
- const latestSignal = (0, import_advanced2.createSignal)(void 0);
2355
+ const dataSignal = (0, import_advanced4.createSignal)(void 0);
2356
+ const loadingSignal = (0, import_advanced4.createSignal)(true);
2357
+ const errorSignal = (0, import_advanced4.createSignal)(void 0);
2358
+ const latestSignal = (0, import_advanced4.createSignal)(void 0);
2180
2359
  let currentSource;
2181
2360
  let fetchId = 0;
2182
2361
  const doFetch = async (s, id) => {
@@ -2185,7 +2364,7 @@ function createResource(source, fetcher) {
2185
2364
  try {
2186
2365
  const result = await fetcher(s);
2187
2366
  if (id === fetchId) {
2188
- (0, import_runtime4.batch)(() => {
2367
+ (0, import_runtime5.batch)(() => {
2189
2368
  dataSignal(result);
2190
2369
  latestSignal(result);
2191
2370
  loadingSignal(false);
@@ -2195,7 +2374,7 @@ function createResource(source, fetcher) {
2195
2374
  return void 0;
2196
2375
  } catch (err) {
2197
2376
  if (id === fetchId) {
2198
- (0, import_runtime4.batch)(() => {
2377
+ (0, import_runtime5.batch)(() => {
2199
2378
  errorSignal(err);
2200
2379
  loadingSignal(false);
2201
2380
  });
@@ -2203,7 +2382,7 @@ function createResource(source, fetcher) {
2203
2382
  return void 0;
2204
2383
  }
2205
2384
  };
2206
- (0, import_runtime4.createEffect)(() => {
2385
+ (0, import_runtime5.createEffect)(() => {
2207
2386
  const s = source();
2208
2387
  if (s !== currentSource) {
2209
2388
  currentSource = s;
@@ -2229,21 +2408,25 @@ function cleanupDataUtilities() {
2229
2408
  }
2230
2409
 
2231
2410
  // src/lazy.tsx
2232
- var import_runtime5 = require("@fictjs/runtime");
2233
- var import_advanced3 = require("@fictjs/runtime/advanced");
2234
- var import_jsx_runtime3 = require("fict/jsx-runtime");
2411
+ var import_runtime6 = require("@fictjs/runtime");
2412
+ var import_advanced5 = require("@fictjs/runtime/advanced");
2413
+ var import_jsx_runtime4 = require("fict/jsx-runtime");
2235
2414
  function lazy(loader) {
2236
2415
  let cachedComponent = null;
2237
2416
  let loadPromise = null;
2238
2417
  const LazyComponent = (props) => {
2239
- const state = (0, import_advanced3.createSignal)({
2418
+ let isMounted = true;
2419
+ (0, import_runtime6.onCleanup)(() => {
2420
+ isMounted = false;
2421
+ });
2422
+ const state = (0, import_advanced5.createSignal)({
2240
2423
  component: cachedComponent,
2241
2424
  error: null,
2242
2425
  loading: !cachedComponent
2243
2426
  });
2244
2427
  if (cachedComponent) {
2245
2428
  const CachedComponent = cachedComponent;
2246
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CachedComponent, { ...props });
2429
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CachedComponent, { ...props });
2247
2430
  }
2248
2431
  if (!loadPromise) {
2249
2432
  loadPromise = loader().then((module2) => {
@@ -2253,9 +2436,13 @@ function lazy(loader) {
2253
2436
  });
2254
2437
  }
2255
2438
  loadPromise.then((component) => {
2256
- state({ component, error: null, loading: false });
2439
+ if (isMounted) {
2440
+ state({ component, error: null, loading: false });
2441
+ }
2257
2442
  }).catch((error) => {
2258
- state({ component: null, error, loading: false });
2443
+ if (isMounted) {
2444
+ state({ component: null, error, loading: false });
2445
+ }
2259
2446
  });
2260
2447
  const currentState = state();
2261
2448
  if (currentState.error) {
@@ -2265,10 +2452,11 @@ function lazy(loader) {
2265
2452
  throw loadPromise;
2266
2453
  }
2267
2454
  const LoadedComponent = currentState.component;
2268
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LoadedComponent, { ...props });
2455
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LoadedComponent, { ...props });
2269
2456
  };
2270
- LazyComponent.__lazy = true;
2271
- LazyComponent.__preload = () => {
2457
+ const lazyComp = LazyComponent;
2458
+ lazyComp.__lazy = true;
2459
+ lazyComp.__preload = () => {
2272
2460
  if (!loadPromise) {
2273
2461
  loadPromise = loader().then((module2) => {
2274
2462
  const component = "default" in module2 ? module2.default : module2;
@@ -2288,7 +2476,8 @@ function preloadLazy(component) {
2288
2476
  return Promise.resolve();
2289
2477
  }
2290
2478
  function isLazyComponent(component) {
2291
- return !!(component && typeof component === "function" && component.__lazy);
2479
+ const comp = component;
2480
+ return !!(comp && typeof comp === "function" && comp.__lazy);
2292
2481
  }
2293
2482
  function lazyRoute(config) {
2294
2483
  const LazyComponent = lazy(config.component);