@pyreon/router 0.12.12 → 0.12.14

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/README.md CHANGED
@@ -98,3 +98,17 @@ const data = useLoaderData<typeof loader>()
98
98
  ### Types
99
99
 
100
100
  `ExtractParams`, `RouteMeta`, `ResolvedRoute`, `RouteRecord`, `RouterOptions`, `Router`, `NavigationGuard`, `AfterEachHook`, `ScrollBehaviorFn`, `LoaderContext`, `RouteLoaderFn`
101
+
102
+ ## View Transitions
103
+
104
+ Route changes are wrapped in `document.startViewTransition()` automatically when the browser supports it. Opt out per-route with `meta: { viewTransition: false }`.
105
+
106
+ `await router.push()` / `.replace()` resolves once the DOM has committed to the new route -- specifically, when the ViewTransition's `updateCallbackDone` promise settles. It does NOT wait for the full animation (`.finished`, 200-300ms), because blocking every programmatic navigation on an animation is unacceptable.
107
+
108
+ | Promise | Resolves when | Router awaits? |
109
+ | --- | --- | --- |
110
+ | `updateCallbackDone` | Callback done; DOM swapped; state live | yes |
111
+ | `ready` | Snapshot captured, pseudo-elements ready | no -- `.catch()` only |
112
+ | `finished` | Full animation completed | no -- `.catch()` only |
113
+
114
+ `afterEach` hooks and scroll restoration fire after the VT callback completes, so they observe the new route state when invoked.
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
5386
5386
  </script>
5387
5387
  <script>
5388
5388
  /*<!--*/
5389
- const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"a014a385-1","name":"loader.ts"},{"uid":"a014a385-3","name":"match.ts"},{"uid":"a014a385-5","name":"scroll.ts"},{"uid":"a014a385-7","name":"types.ts"},{"uid":"a014a385-9","name":"router.ts"},{"uid":"a014a385-11","name":"components.tsx"},{"uid":"a014a385-13","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"a014a385-1":{"renderedLength":2855,"gzipLength":1243,"brotliLength":0,"metaUid":"a014a385-0"},"a014a385-3":{"renderedLength":12203,"gzipLength":3691,"brotliLength":0,"metaUid":"a014a385-2"},"a014a385-5":{"renderedLength":1816,"gzipLength":765,"brotliLength":0,"metaUid":"a014a385-4"},"a014a385-7":{"renderedLength":385,"gzipLength":246,"brotliLength":0,"metaUid":"a014a385-6"},"a014a385-9":{"renderedLength":22335,"gzipLength":6159,"brotliLength":0,"metaUid":"a014a385-8"},"a014a385-11":{"renderedLength":7106,"gzipLength":2622,"brotliLength":0,"metaUid":"a014a385-10"},"a014a385-13":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"a014a385-12"}},"nodeMetas":{"a014a385-0":{"id":"/src/loader.ts","moduleParts":{"index.js":"a014a385-1"},"imported":[{"uid":"a014a385-14"}],"importedBy":[{"uid":"a014a385-12"},{"uid":"a014a385-10"}]},"a014a385-2":{"id":"/src/match.ts","moduleParts":{"index.js":"a014a385-3"},"imported":[],"importedBy":[{"uid":"a014a385-12"},{"uid":"a014a385-8"}]},"a014a385-4":{"id":"/src/scroll.ts","moduleParts":{"index.js":"a014a385-5"},"imported":[],"importedBy":[{"uid":"a014a385-8"}]},"a014a385-6":{"id":"/src/types.ts","moduleParts":{"index.js":"a014a385-7"},"imported":[],"importedBy":[{"uid":"a014a385-12"},{"uid":"a014a385-8"}]},"a014a385-8":{"id":"/src/router.ts","moduleParts":{"index.js":"a014a385-9"},"imported":[{"uid":"a014a385-14"},{"uid":"a014a385-15"},{"uid":"a014a385-2"},{"uid":"a014a385-4"},{"uid":"a014a385-6"}],"importedBy":[{"uid":"a014a385-12"},{"uid":"a014a385-10"}]},"a014a385-10":{"id":"/src/components.tsx","moduleParts":{"index.js":"a014a385-11"},"imported":[{"uid":"a014a385-14"},{"uid":"a014a385-0"},{"uid":"a014a385-8"}],"importedBy":[{"uid":"a014a385-12"}]},"a014a385-12":{"id":"/src/index.ts","moduleParts":{"index.js":"a014a385-13"},"imported":[{"uid":"a014a385-10"},{"uid":"a014a385-0"},{"uid":"a014a385-2"},{"uid":"a014a385-8"},{"uid":"a014a385-6"}],"importedBy":[],"isEntry":true},"a014a385-14":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"a014a385-10"},{"uid":"a014a385-0"},{"uid":"a014a385-8"}]},"a014a385-15":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"a014a385-8"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5389
+ const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"d7a354cf-1","name":"loader.ts"},{"uid":"d7a354cf-3","name":"match.ts"},{"uid":"d7a354cf-5","name":"scroll.ts"},{"uid":"d7a354cf-7","name":"types.ts"},{"uid":"d7a354cf-9","name":"router.ts"},{"uid":"d7a354cf-11","name":"components.tsx"},{"uid":"d7a354cf-13","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"d7a354cf-1":{"renderedLength":2855,"gzipLength":1243,"brotliLength":0,"metaUid":"d7a354cf-0"},"d7a354cf-3":{"renderedLength":12203,"gzipLength":3691,"brotliLength":0,"metaUid":"d7a354cf-2"},"d7a354cf-5":{"renderedLength":1906,"gzipLength":781,"brotliLength":0,"metaUid":"d7a354cf-4"},"d7a354cf-7":{"renderedLength":385,"gzipLength":246,"brotliLength":0,"metaUid":"d7a354cf-6"},"d7a354cf-9":{"renderedLength":23190,"gzipLength":6358,"brotliLength":0,"metaUid":"d7a354cf-8"},"d7a354cf-11":{"renderedLength":7106,"gzipLength":2622,"brotliLength":0,"metaUid":"d7a354cf-10"},"d7a354cf-13":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"d7a354cf-12"}},"nodeMetas":{"d7a354cf-0":{"id":"/src/loader.ts","moduleParts":{"index.js":"d7a354cf-1"},"imported":[{"uid":"d7a354cf-14"}],"importedBy":[{"uid":"d7a354cf-12"},{"uid":"d7a354cf-10"}]},"d7a354cf-2":{"id":"/src/match.ts","moduleParts":{"index.js":"d7a354cf-3"},"imported":[],"importedBy":[{"uid":"d7a354cf-12"},{"uid":"d7a354cf-8"}]},"d7a354cf-4":{"id":"/src/scroll.ts","moduleParts":{"index.js":"d7a354cf-5"},"imported":[],"importedBy":[{"uid":"d7a354cf-8"}]},"d7a354cf-6":{"id":"/src/types.ts","moduleParts":{"index.js":"d7a354cf-7"},"imported":[],"importedBy":[{"uid":"d7a354cf-12"},{"uid":"d7a354cf-8"}]},"d7a354cf-8":{"id":"/src/router.ts","moduleParts":{"index.js":"d7a354cf-9"},"imported":[{"uid":"d7a354cf-14"},{"uid":"d7a354cf-15"},{"uid":"d7a354cf-2"},{"uid":"d7a354cf-4"},{"uid":"d7a354cf-6"}],"importedBy":[{"uid":"d7a354cf-12"},{"uid":"d7a354cf-10"}]},"d7a354cf-10":{"id":"/src/components.tsx","moduleParts":{"index.js":"d7a354cf-11"},"imported":[{"uid":"d7a354cf-14"},{"uid":"d7a354cf-0"},{"uid":"d7a354cf-8"}],"importedBy":[{"uid":"d7a354cf-12"}]},"d7a354cf-12":{"id":"/src/index.ts","moduleParts":{"index.js":"d7a354cf-13"},"imported":[{"uid":"d7a354cf-10"},{"uid":"d7a354cf-0"},{"uid":"d7a354cf-2"},{"uid":"d7a354cf-8"},{"uid":"d7a354cf-6"}],"importedBy":[],"isEntry":true},"d7a354cf-14":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"d7a354cf-10"},{"uid":"d7a354cf-0"},{"uid":"d7a354cf-8"}]},"d7a354cf-15":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"d7a354cf-8"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
5390
5390
 
5391
5391
  const run = () => {
5392
5392
  const width = window.innerWidth;
package/lib/index.js CHANGED
@@ -506,6 +506,7 @@ var ScrollManager = class {
506
506
  }
507
507
  /** Call before navigating away — saves current scroll position for `fromPath` */
508
508
  save(fromPath) {
509
+ if (typeof window === "undefined") return;
509
510
  this._positions.set(fromPath, window.scrollY);
510
511
  }
511
512
  /** Call after navigation is committed — applies scroll behavior */
@@ -519,6 +520,7 @@ var ScrollManager = class {
519
520
  this._applyResult(behavior, to.path);
520
521
  }
521
522
  _applyResult(result, toPath) {
523
+ if (typeof window === "undefined") return;
522
524
  const hashIdx = toPath.indexOf("#");
523
525
  if (hashIdx >= 0) {
524
526
  const id = toPath.slice(hashIdx + 1);
@@ -588,12 +590,12 @@ function setActiveRouter(router) {
588
590
  }
589
591
  function useRouter() {
590
592
  const router = useContext(RouterContext) ?? _activeRouter;
591
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
593
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
592
594
  return router;
593
595
  }
594
596
  function useRoute() {
595
597
  const router = useContext(RouterContext) ?? _activeRouter;
596
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
598
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
597
599
  return router.currentRoute;
598
600
  }
599
601
  /**
@@ -608,7 +610,7 @@ function useRoute() {
608
610
  */
609
611
  function onBeforeRouteLeave(guard) {
610
612
  const router = useContext(RouterContext) ?? _activeRouter;
611
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
613
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
612
614
  const currentMatched = router.currentRoute().matched;
613
615
  const wrappedGuard = (to, from) => {
614
616
  if (!from.matched.some((r) => currentMatched.includes(r))) return void 0;
@@ -630,7 +632,7 @@ function onBeforeRouteLeave(guard) {
630
632
  */
631
633
  function onBeforeRouteUpdate(guard) {
632
634
  const router = useContext(RouterContext) ?? _activeRouter;
633
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
635
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
634
636
  const currentMatched = router.currentRoute().matched;
635
637
  const wrappedGuard = (to, from) => {
636
638
  if (!to.matched.some((r) => currentMatched.includes(r))) return void 0;
@@ -656,7 +658,7 @@ function onBeforeRouteUpdate(guard) {
656
658
  */
657
659
  function useBlocker(fn) {
658
660
  const router = useContext(RouterContext) ?? _activeRouter;
659
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
661
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
660
662
  router._blockers.add(fn);
661
663
  const beforeUnloadHandler = _isBrowser ? (e) => {
662
664
  e.preventDefault();
@@ -699,7 +701,7 @@ function useBlocker(fn) {
699
701
  */
700
702
  function useIsActive(path, exact = false) {
701
703
  const router = useContext(RouterContext) ?? _activeRouter;
702
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
704
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
703
705
  return () => {
704
706
  const current = router.currentRoute().path;
705
707
  if (exact) return matchSegments(current, path, true);
@@ -804,7 +806,7 @@ function useTypedSearchParams(schema) {
804
806
  }
805
807
  function _getRouter() {
806
808
  const router = useContext(RouterContext) ?? _activeRouter;
807
- if (!router) throw new Error("[pyreon-router] No router installed. Wrap your app in <RouterProvider router={router}>.");
809
+ if (!router) throw new Error("[Pyreon] No router installed. Wrap your app in <RouterProvider router={router}>.");
808
810
  return router;
809
811
  }
810
812
  /**
@@ -867,15 +869,10 @@ function createRouter(options) {
867
869
  };
868
870
  const currentPath = signal(normalizeTrailingSlash(getInitialLocation(), trailingSlash));
869
871
  const currentRoute = computed(() => resolveRoute(currentPath(), routes));
870
- let _popstateHandler = null;
871
- let _hashchangeHandler = null;
872
- if (_isBrowser) if (mode === "history") {
873
- _popstateHandler = () => currentPath.set(getCurrentLocation());
874
- window.addEventListener("popstate", _popstateHandler);
875
- } else {
876
- _hashchangeHandler = () => currentPath.set(getCurrentLocation());
877
- window.addEventListener("hashchange", _hashchangeHandler);
878
- }
872
+ const _popstateHandler = _isBrowser && mode === "history" ? () => currentPath.set(getCurrentLocation()) : null;
873
+ const _hashchangeHandler = _isBrowser && mode !== "history" ? () => currentPath.set(getCurrentLocation()) : null;
874
+ if (_popstateHandler) window.addEventListener("popstate", _popstateHandler);
875
+ if (_hashchangeHandler) window.addEventListener("hashchange", _hashchangeHandler);
879
876
  const componentCache = /* @__PURE__ */ new Map();
880
877
  const loadingSignal = signal(0);
881
878
  async function evaluateGuard(guard, to, from, gen) {
@@ -984,7 +981,7 @@ function createRouter(options) {
984
981
  if (swr.length > 0) revalidateSwrLoaders(swr, to, ac);
985
982
  return true;
986
983
  }
987
- function commitNavigation(path, replace, to, from) {
984
+ async function commitNavigation(path, replace, to, from) {
988
985
  scrollManager.save(from.path);
989
986
  const doCommit = () => {
990
987
  currentPath.set(path);
@@ -992,10 +989,18 @@ function createRouter(options) {
992
989
  if (_isBrowser && to.meta.title) document.title = to.meta.title;
993
990
  for (const record of router._loaderData.keys()) if (!to.matched.includes(record)) router._loaderData.delete(record);
994
991
  };
995
- if (_isBrowser && to.meta.viewTransition !== false && typeof document.startViewTransition === "function") document.startViewTransition(() => {
996
- doCommit();
997
- });
998
- else doCommit();
992
+ if (_isBrowser && to.meta.viewTransition !== false && typeof document.startViewTransition === "function") {
993
+ const vt = document.startViewTransition(() => {
994
+ doCommit();
995
+ });
996
+ if (vt) {
997
+ vt.ready?.catch(() => {});
998
+ vt.finished?.catch(() => {});
999
+ if (vt.updateCallbackDone) try {
1000
+ await vt.updateCallbackDone;
1001
+ } catch {}
1002
+ }
1003
+ } else doCommit();
999
1004
  for (const hook of afterHooks) try {
1000
1005
  hook(to, from);
1001
1006
  } catch (err) {
@@ -1071,7 +1076,7 @@ function createRouter(options) {
1071
1076
  loadingSignal.update((n) => n - 1);
1072
1077
  return;
1073
1078
  }
1074
- commitNavigation(path, replace, to, from);
1079
+ await commitNavigation(path, replace, to, from);
1075
1080
  loadingSignal.update((n) => n - 1);
1076
1081
  }
1077
1082
  let _readyResolve = null;
@@ -1133,15 +1138,33 @@ function createRouter(options) {
1133
1138
  isReady() {
1134
1139
  return router._readyPromise;
1135
1140
  },
1141
+ async preload(path) {
1142
+ const resolved = resolveRoute(path, routes);
1143
+ await Promise.all(resolved.matched.map(async (record) => {
1144
+ if (componentCache.has(record)) return;
1145
+ const raw = record.component;
1146
+ if (!isLazy(raw)) {
1147
+ componentCache.set(record, raw);
1148
+ return;
1149
+ }
1150
+ const mod = await raw.loader();
1151
+ const comp = typeof mod === "function" ? mod : mod.default;
1152
+ componentCache.set(record, comp);
1153
+ }));
1154
+ const ac = new AbortController();
1155
+ router._abortController = ac;
1156
+ await Promise.all(resolved.matched.filter((r) => r.loader).map(async (r) => {
1157
+ const data = await r.loader?.({
1158
+ params: resolved.params,
1159
+ query: resolved.query,
1160
+ signal: ac.signal
1161
+ });
1162
+ router._loaderData.set(r, data);
1163
+ }));
1164
+ },
1136
1165
  destroy() {
1137
- if (_popstateHandler) {
1138
- window.removeEventListener("popstate", _popstateHandler);
1139
- _popstateHandler = null;
1140
- }
1141
- if (_hashchangeHandler) {
1142
- window.removeEventListener("hashchange", _hashchangeHandler);
1143
- _hashchangeHandler = null;
1144
- }
1166
+ if (_popstateHandler) window.removeEventListener("popstate", _popstateHandler);
1167
+ if (_hashchangeHandler) window.removeEventListener("hashchange", _hashchangeHandler);
1145
1168
  guards.length = 0;
1146
1169
  afterHooks.length = 0;
1147
1170
  router._blockers.clear();