@solidjs/router 0.9.1 → 0.10.0-beta.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.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { isServer, delegateEvents, getRequestEvent, createComponent as createComponent$1, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
2
- import { createSignal, onCleanup, getOwner, runWithOwner, createMemo, createContext, useContext, untrack, createRenderEffect, createComponent, on, startTransition, resetErrorBoundaries, children, createRoot, Show, mergeProps, splitProps } from 'solid-js';
2
+ import { createSignal, onCleanup, getOwner, runWithOwner, createMemo, createContext, useContext, untrack, createRenderEffect, on, startTransition, createComponent, resetErrorBoundaries, children, createRoot, Show, mergeProps, splitProps, createResource, sharedConfig, $TRACK } from 'solid-js';
3
+ import { createStore, reconcile } from 'solid-js/store';
3
4
 
4
5
  function bindEvent(target, type, handler) {
5
6
  target.addEventListener(type, handler);
@@ -201,6 +202,7 @@ function createBeforeLeave() {
201
202
 
202
203
  const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
203
204
  const trimPathRegex = /^\/+|(\/)\/+$/g;
205
+ const redirectStatusCodes = new Set([204, 301, 302, 303, 307, 308]);
204
206
  function normalizePath(path, omitSlash = false) {
205
207
  const s = path.replace(trimPathRegex, "$1");
206
208
  return s ? omitSlash || /^[?#]/.test(s) ? s : "/" + s : "";
@@ -353,8 +355,7 @@ const MAX_REDIRECTS = 100;
353
355
  const RouterContextObj = createContext();
354
356
  const RouteContextObj = createContext();
355
357
  const useRouter = () => invariant(useContext(RouterContextObj), "Make sure your app is wrapped in a <Router />");
356
- let TempRoute;
357
- const useRoute = () => TempRoute || useContext(RouteContextObj) || useRouter().base;
358
+ const useRoute = () => useContext(RouteContextObj) || useRouter().base;
358
359
  const useResolvedPath = path => {
359
360
  const route = useRoute();
360
361
  return createMemo(() => route.resolvePath(path()));
@@ -380,7 +381,6 @@ const useMatch = (path, matchFilters) => {
380
381
  });
381
382
  };
382
383
  const useParams = () => useRoute().params;
383
- const useRouteData = () => useRoute().data;
384
384
  const useSearchParams = () => {
385
385
  const location = useLocation();
386
386
  const navigate = useNavigate();
@@ -402,23 +402,17 @@ const useBeforeLeave = listener => {
402
402
  });
403
403
  onCleanup(s);
404
404
  };
405
- function createRoutes(routeDef, base = "", fallback) {
405
+ function createRoutes(routeDef, base = "") {
406
406
  const {
407
407
  component,
408
- data,
408
+ load,
409
409
  children
410
410
  } = routeDef;
411
411
  const isLeaf = !children || Array.isArray(children) && !children.length;
412
412
  const shared = {
413
413
  key: routeDef,
414
- element: component ? () => createComponent(component, {}) : () => {
415
- const {
416
- element
417
- } = routeDef;
418
- return element === undefined && fallback ? createComponent(fallback, {}) : element;
419
- },
420
- preload: routeDef.component ? component.preload : routeDef.preload,
421
- data
414
+ component,
415
+ load
422
416
  };
423
417
  return asArray(routeDef.path).reduce((acc, path) => {
424
418
  for (const originalPath of expandOptionals(path)) {
@@ -458,17 +452,18 @@ function createBranch(routes, index = 0) {
458
452
  function asArray(value) {
459
453
  return Array.isArray(value) ? value : [value];
460
454
  }
461
- function createBranches(routeDef, base = "", fallback, stack = [], branches = []) {
455
+ function createBranches(routeDef, base = "", stack = [], branches = []) {
462
456
  const routeDefs = asArray(routeDef);
463
457
  for (let i = 0, len = routeDefs.length; i < len; i++) {
464
458
  const def = routeDefs[i];
465
- if (def && typeof def === "object" && def.hasOwnProperty("path")) {
466
- const routes = createRoutes(def, base, fallback);
459
+ if (def && typeof def === "object") {
460
+ if (!def.hasOwnProperty("path")) def.path = "";
461
+ const routes = createRoutes(def, base);
467
462
  for (const route of routes) {
468
463
  stack.push(route);
469
464
  const isEmptyArray = Array.isArray(def.children) && def.children.length === 0;
470
465
  if (def.children && !isEmptyArray) {
471
- createBranches(def.children, route.pattern, fallback, stack, branches);
466
+ createBranches(def.children, route.pattern, stack, branches);
472
467
  } else {
473
468
  const branch = createBranch([...stack], branches.length);
474
469
  branches.push(branch);
@@ -506,7 +501,7 @@ function createLocation(path, state) {
506
501
  const pathname = createMemo(() => url().pathname);
507
502
  const search = createMemo(() => url().search, true);
508
503
  const hash = createMemo(() => url().hash);
509
- const key = createMemo(() => "");
504
+ const key = () => "";
510
505
  return {
511
506
  get pathname() {
512
507
  return pathname();
@@ -526,7 +521,15 @@ function createLocation(path, state) {
526
521
  query: createMemoObject(on(search, () => extractSearchParams(url())))
527
522
  };
528
523
  }
529
- function createRouterContext(integration, base = "", data, out) {
524
+ const actions = new Map();
525
+ function registerAction(url, fn) {
526
+ actions.set(url, fn);
527
+ }
528
+ let intent;
529
+ function getIntent() {
530
+ return intent;
531
+ }
532
+ function createRouterContext(integration, getBranches, base = "") {
530
533
  const {
531
534
  signal: [source, setSource],
532
535
  utils = {}
@@ -534,11 +537,8 @@ function createRouterContext(integration, base = "", data, out) {
534
537
  const parsePath = utils.parsePath || (p => p);
535
538
  const renderPath = utils.renderPath || (p => p);
536
539
  const beforeLeave = utils.beforeLeave || createBeforeLeave();
540
+ let submissions = [];
537
541
  const basePath = resolvePath("", base);
538
- const output = isServer && out ? Object.assign(out, {
539
- matches: [],
540
- url: undefined
541
- }) : undefined;
542
542
  if (basePath === undefined) {
543
543
  throw new Error(`${basePath} is not a valid base path`);
544
544
  } else if (basePath && !source().value) {
@@ -570,19 +570,6 @@ function createRouterContext(integration, base = "", data, out) {
570
570
  return resolvePath(basePath, to);
571
571
  }
572
572
  };
573
- if (data) {
574
- try {
575
- TempRoute = baseRoute;
576
- baseRoute.data = data({
577
- data: undefined,
578
- params: {},
579
- location,
580
- navigate: navigatorFactory(baseRoute)
581
- });
582
- } finally {
583
- TempRoute = undefined;
584
- }
585
- }
586
573
  function navigateFromRoute(route, to, options) {
587
574
  // Untrack in case someone navigates in an effect - don't want to track `reference` or route paths
588
575
  untrack(() => {
@@ -614,9 +601,6 @@ function createRouterContext(integration, base = "", data, out) {
614
601
  const current = reference();
615
602
  if (resolvedTo !== current || nextState !== state()) {
616
603
  if (isServer) {
617
- if (output) {
618
- output.url = resolvedTo;
619
- }
620
604
  const e = getRequestEvent();
621
605
  e && (e.response = Response.redirect(resolvedTo, 302));
622
606
  setSource({
@@ -633,11 +617,13 @@ function createRouterContext(integration, base = "", data, out) {
633
617
  state: state()
634
618
  });
635
619
  start(() => {
620
+ intent = "navigate";
636
621
  setReference(resolvedTo);
637
622
  setState(nextState);
638
623
  resetErrorBoundaries();
639
624
  }).then(() => {
640
625
  if (referrers.length === len) {
626
+ intent = undefined;
641
627
  navigateEnd({
642
628
  value: resolvedTo,
643
629
  state: nextState
@@ -675,23 +661,38 @@ function createRouterContext(integration, base = "", data, out) {
675
661
  untrack(() => {
676
662
  if (value !== reference()) {
677
663
  start(() => {
664
+ intent = "native";
678
665
  setReference(value);
679
666
  setState(state);
667
+ }).then(() => {
668
+ intent = undefined;
680
669
  });
681
670
  }
682
671
  });
683
672
  });
684
673
  if (!isServer) {
685
- function handleAnchorClick(evt) {
674
+ let preloadTimeout = {};
675
+ function isSvg(el) {
676
+ return el.namespaceURI === "http://www.w3.org/2000/svg";
677
+ }
678
+ function handleAnchor(evt) {
686
679
  if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
687
680
  const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
688
- if (!a || !a.hasAttribute("link")) return;
689
- const href = a.href;
690
- if (a.target || !href && !a.hasAttribute("state")) return;
681
+ if (!a) return;
682
+ const svg = isSvg(a);
683
+ const href = svg ? a.href.baseVal : a.href;
684
+ const target = svg ? a.target.baseVal : a.target;
685
+ if (target || !href && !a.hasAttribute("state")) return;
691
686
  const rel = (a.getAttribute("rel") || "").split(/\s+/);
692
687
  if (a.hasAttribute("download") || rel && rel.includes("external")) return;
693
- const url = new URL(href);
688
+ const url = svg ? new URL(href, document.baseURI) : new URL(href);
694
689
  if (url.origin !== window.location.origin || basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())) return;
690
+ return [a, url];
691
+ }
692
+ function handleAnchorClick(evt) {
693
+ const res = handleAnchor(evt);
694
+ if (!res) return;
695
+ const [a, url] = res;
695
696
  const to = parsePath(url.pathname + url.search + url.hash);
696
697
  const state = a.getAttribute("state");
697
698
  evt.preventDefault();
@@ -702,94 +703,175 @@ function createRouterContext(integration, base = "", data, out) {
702
703
  state: state && JSON.parse(state)
703
704
  });
704
705
  }
706
+ function doPreload(a, url) {
707
+ const preload = a.getAttribute("preload") !== "false";
708
+ const matches = getRouteMatches(getBranches(), url.pathname);
709
+ const prevIntent = intent;
710
+ intent = "preload";
711
+ for (let match in matches) {
712
+ const {
713
+ route,
714
+ params
715
+ } = matches[match];
716
+ route.component && route.component.preload && route.component.preload();
717
+ preload && route.load && route.load({
718
+ params,
719
+ location: {
720
+ pathname: url.pathname,
721
+ search: url.search,
722
+ hash: url.hash,
723
+ query: extractSearchParams(url),
724
+ state: null,
725
+ key: ""
726
+ },
727
+ intent
728
+ });
729
+ }
730
+ intent = prevIntent;
731
+ }
732
+ function handleAnchorPreload(evt) {
733
+ const res = handleAnchor(evt);
734
+ if (!res) return;
735
+ const [a, url] = res;
736
+ if (!preloadTimeout[url.pathname]) doPreload(a, url);
737
+ }
738
+ function handleAnchorIn(evt) {
739
+ const res = handleAnchor(evt);
740
+ if (!res) return;
741
+ const [a, url] = res;
742
+ if (preloadTimeout[url.pathname]) return;
743
+ preloadTimeout[url.pathname] = setTimeout(() => {
744
+ doPreload(a, url);
745
+ delete preloadTimeout[url.pathname];
746
+ }, 200);
747
+ }
748
+ function handleAnchorOut(evt) {
749
+ const res = handleAnchor(evt);
750
+ if (!res) return;
751
+ const [, url] = res;
752
+ if (preloadTimeout[url.pathname]) {
753
+ clearTimeout(preloadTimeout[url.pathname]);
754
+ delete preloadTimeout[url.pathname];
755
+ }
756
+ }
757
+ function handleFormSubmit(evt) {
758
+ let actionRef = evt.submitter && evt.submitter.getAttribute("formaction") || evt.target.action;
759
+ if (actionRef && actionRef.startsWith("action:")) {
760
+ const data = new FormData(evt.target);
761
+ actions.get(actionRef.slice(7))(data);
762
+ evt.preventDefault();
763
+ }
764
+ }
705
765
 
706
- // ensure delegated events run first
707
- delegateEvents(["click"]);
766
+ // ensure delegated event run first
767
+ delegateEvents(["click", "submit"]);
708
768
  document.addEventListener("click", handleAnchorClick);
709
- onCleanup(() => document.removeEventListener("click", handleAnchorClick));
769
+ document.addEventListener("mouseover", handleAnchorIn);
770
+ document.addEventListener("mouseout", handleAnchorOut);
771
+ document.addEventListener("focusin", handleAnchorPreload);
772
+ document.addEventListener("touchstart", handleAnchorPreload);
773
+ document.addEventListener("submit", handleFormSubmit);
774
+ onCleanup(() => {
775
+ document.removeEventListener("click", handleAnchorClick);
776
+ document.removeEventListener("mouseover", handleAnchorIn);
777
+ document.removeEventListener("mouseout", handleAnchorOut);
778
+ document.removeEventListener("focusin", handleAnchorPreload);
779
+ document.removeEventListener("touchstart", handleAnchorPreload);
780
+ document.removeEventListener("submit", handleFormSubmit);
781
+ });
782
+ } else {
783
+ function initFromFlash(params) {
784
+ let param = params.form ? JSON.parse(params.form) : null;
785
+ if (!param || !param.result) {
786
+ return [];
787
+ }
788
+ const input = new Map(param.entries);
789
+ return [{
790
+ url: param.url,
791
+ result: param.error ? new Error(param.result.message) : param.result,
792
+ input: input
793
+ }];
794
+ }
795
+ submissions = initFromFlash(location.query);
710
796
  }
711
797
  return {
712
798
  base: baseRoute,
713
- out: output,
714
799
  location,
715
800
  isRouting,
716
801
  renderPath,
717
802
  parsePath,
718
803
  navigatorFactory,
719
- beforeLeave
804
+ beforeLeave,
805
+ submissions: createSignal(submissions)
720
806
  };
721
807
  }
722
- function createRouteContext(router, parent, child, match, params) {
808
+ function createRouteContext(router, parent, outlet, match, params) {
723
809
  const {
724
810
  base,
725
- location,
726
- navigatorFactory
811
+ location
727
812
  } = router;
728
813
  const {
729
814
  pattern,
730
- element: outlet,
731
- preload,
732
- data
815
+ component,
816
+ load
733
817
  } = match().route;
734
818
  const path = createMemo(() => match().path);
735
- preload && preload();
736
819
  const route = {
737
820
  parent,
738
821
  pattern,
739
- get child() {
740
- return child();
741
- },
742
822
  path,
743
823
  params,
744
- data: parent.data,
745
- outlet,
824
+ outlet: () => component ? createComponent(component, {
825
+ params,
826
+ location,
827
+ get children() {
828
+ return outlet();
829
+ }
830
+ }) : outlet(),
746
831
  resolvePath(to) {
747
832
  return resolvePath(base.path(), to, path());
748
833
  }
749
834
  };
750
- if (data) {
751
- try {
752
- TempRoute = route;
753
- route.data = data({
754
- data: parent.data,
755
- params,
756
- location,
757
- navigate: navigatorFactory(route)
758
- });
759
- } finally {
760
- TempRoute = undefined;
761
- }
762
- }
835
+ component && component.preload && component.preload();
836
+ load && load({
837
+ params,
838
+ location,
839
+ intent: intent || "navigate"
840
+ });
763
841
  return route;
764
842
  }
765
843
 
766
- const _tmpl$ = /*#__PURE__*/template(`<a link>`);
844
+ const _tmpl$ = /*#__PURE__*/template(`<a></a>`, 2);
767
845
  const Router = props => {
768
846
  let e;
769
847
  const {
770
848
  source,
771
849
  url,
772
- base,
773
- data,
774
- out
850
+ base
775
851
  } = props;
776
852
  const integration = source || (isServer ? staticIntegration({
777
853
  value: url || (e = getRequestEvent()) && e.request.url || ""
778
854
  }) : pathIntegration());
779
- const routerState = createRouterContext(integration, base, data, out);
855
+ const routeDefs = children(() => props.children);
856
+ const branches = createMemo(() => createBranches(props.root ? {
857
+ component: props.root,
858
+ children: routeDefs()
859
+ } : routeDefs(), props.base || ""));
860
+ const routerState = createRouterContext(integration, branches, base);
780
861
  return createComponent$1(RouterContextObj.Provider, {
781
862
  value: routerState,
782
863
  get children() {
783
- return props.children;
864
+ return createComponent$1(Routes, {
865
+ routerState: routerState,
866
+ get branches() {
867
+ return branches();
868
+ }
869
+ });
784
870
  }
785
871
  });
786
872
  };
787
- const Routes = props => {
788
- const router = useRouter();
789
- const parentRoute = useRoute();
790
- const routeDefs = children(() => props.children);
791
- const branches = createMemo(() => createBranches(routeDefs(), joinPaths(parentRoute.pattern, props.base || ""), Outlet));
792
- const matches = createMemo(() => getRouteMatches(branches(), router.location.pathname));
873
+ function Routes(props) {
874
+ const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
793
875
  const params = createMemoObject(() => {
794
876
  const m = matches();
795
877
  const params = {};
@@ -798,18 +880,6 @@ const Routes = props => {
798
880
  }
799
881
  return params;
800
882
  });
801
- if (router.out) {
802
- router.out.matches.push(matches().map(({
803
- route,
804
- path,
805
- params
806
- }) => ({
807
- originalPath: route.originalPath,
808
- pattern: route.pattern,
809
- path,
810
- params
811
- })));
812
- }
813
883
  const disposers = [];
814
884
  let root;
815
885
  const routeStates = createMemo(on(matches, (nextMatches, prevMatches, prev) => {
@@ -827,7 +897,7 @@ const Routes = props => {
827
897
  }
828
898
  createRoot(dispose => {
829
899
  disposers[i] = dispose;
830
- next[i] = createRouteContext(router, next[i - 1] || parentRoute, () => routeStates()[i + 1], () => matches()[i], params);
900
+ next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => matches()[i], params);
831
901
  });
832
902
  }
833
903
  }
@@ -850,26 +920,11 @@ const Routes = props => {
850
920
  }
851
921
  })
852
922
  });
853
- };
854
- const useRoutes = (routes, base) => {
855
- return () => createComponent$1(Routes, {
856
- base: base,
857
- children: routes
858
- });
859
- };
860
- const Route = props => {
861
- const childRoutes = children(() => props.children);
862
- return mergeProps(props, {
863
- get children() {
864
- return childRoutes();
865
- }
866
- });
867
- };
868
- const Outlet = () => {
869
- const route = useRoute();
870
- return createComponent$1(Show, {
923
+ }
924
+ const createOutlet = child => {
925
+ return () => createComponent$1(Show, {
871
926
  get when() {
872
- return route.child;
927
+ return child();
873
928
  },
874
929
  keyed: true,
875
930
  children: child => createComponent$1(RouteContextObj.Provider, {
@@ -880,6 +935,14 @@ const Outlet = () => {
880
935
  })
881
936
  });
882
937
  };
938
+ const Route = props => {
939
+ const childRoutes = children(() => props.children);
940
+ return mergeProps(props, {
941
+ get children() {
942
+ return childRoutes();
943
+ }
944
+ });
945
+ };
883
946
  function A(props) {
884
947
  props = mergeProps({
885
948
  inactiveClass: "inactive",
@@ -897,7 +960,7 @@ function A(props) {
897
960
  return props.end ? path === loc : loc.startsWith(path);
898
961
  });
899
962
  return (() => {
900
- const _el$ = _tmpl$();
963
+ const _el$ = _tmpl$.cloneNode(true);
901
964
  spread(_el$, mergeProps$1(rest, {
902
965
  get href() {
903
966
  return href() || props.href;
@@ -940,4 +1003,261 @@ function Navigate(props) {
940
1003
  return null;
941
1004
  }
942
1005
 
943
- export { A, A as Link, A as NavLink, Navigate, Outlet, Route, Router, Routes, mergeSearchString as _mergeSearchString, createBeforeLeave, createIntegration, createMemoryHistory, hashIntegration, memoryIntegration, normalizeIntegration, pathIntegration, staticIntegration, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useRouteData, useRoutes, useSearchParams };
1006
+ /**
1007
+ * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
1008
+ */
1009
+ function createAsync(fn) {
1010
+ const [resource] = createResource(() => subFetch(fn), v => v);
1011
+ return () => resource();
1012
+ }
1013
+
1014
+ // mock promise while hydrating to prevent fetching
1015
+ class MockPromise {
1016
+ static all() {
1017
+ return new MockPromise();
1018
+ }
1019
+ static allSettled() {
1020
+ return new MockPromise();
1021
+ }
1022
+ static any() {
1023
+ return new MockPromise();
1024
+ }
1025
+ static race() {
1026
+ return new MockPromise();
1027
+ }
1028
+ static reject() {
1029
+ return new MockPromise();
1030
+ }
1031
+ static resolve() {
1032
+ return new MockPromise();
1033
+ }
1034
+ catch() {
1035
+ return new MockPromise();
1036
+ }
1037
+ then() {
1038
+ return new MockPromise();
1039
+ }
1040
+ finally() {
1041
+ return new MockPromise();
1042
+ }
1043
+ }
1044
+ function subFetch(fn) {
1045
+ if (isServer || !sharedConfig.context) return fn();
1046
+ const ogFetch = fetch;
1047
+ const ogPromise = Promise;
1048
+ try {
1049
+ window.fetch = () => new MockPromise();
1050
+ Promise = MockPromise;
1051
+ return fn();
1052
+ } finally {
1053
+ window.fetch = ogFetch;
1054
+ Promise = ogPromise;
1055
+ }
1056
+ }
1057
+
1058
+ const LocationHeader = "Location";
1059
+ const PRELOAD_TIMEOUT = 5000;
1060
+ let cacheMap = new Map();
1061
+ function getCache() {
1062
+ if (!isServer) return cacheMap;
1063
+ const req = getRequestEvent() || sharedConfig.context;
1064
+ return req.routerCache || (req.routerCache = new Map());
1065
+ }
1066
+ function revalidate(key) {
1067
+ return startTransition(() => {
1068
+ const now = Date.now();
1069
+ for (let k of cacheMap.keys()) {
1070
+ if (key === undefined || k === key) {
1071
+ const set = cacheMap.get(k)[3];
1072
+ revalidateSignals(set, now);
1073
+ cacheMap.delete(k);
1074
+ }
1075
+ }
1076
+ });
1077
+ }
1078
+ function revalidateSignals(set, time) {
1079
+ for (let s of set) s[1](time);
1080
+ }
1081
+ function cache(fn, name, options) {
1082
+ const [store, setStore] = createStore({});
1083
+ return (...args) => {
1084
+ const cache = getCache();
1085
+ const intent = getIntent();
1086
+ const owner = getOwner();
1087
+ const navigate = owner ? useNavigate() : undefined;
1088
+ const now = Date.now();
1089
+ const key = name + (args.length ? ":" + args.join(":") : "");
1090
+ let cached = cache.get(key);
1091
+ let version;
1092
+ if (owner) {
1093
+ version = createSignal(now, {
1094
+ equals: (p, v) => v - p < 50 // margin of error
1095
+ });
1096
+
1097
+ onCleanup(() => cached[3].delete(version));
1098
+ version[0](); // track it;
1099
+ }
1100
+
1101
+ if (cached && (isServer || intent === "native" || Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
1102
+ version && cached[3].add(version);
1103
+ if (cached[2] === "preload" && intent !== "preload") {
1104
+ cached[0] = now;
1105
+ cached[1] = "then" in cached[1] ? cached[1].then(handleResponse) : handleResponse(cached[1]);
1106
+ cached[2] = intent;
1107
+ }
1108
+ if (!isServer && intent === "navigate") {
1109
+ startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
1110
+ }
1111
+
1112
+ return cached[1];
1113
+ }
1114
+ let res = !isServer && sharedConfig.context && sharedConfig.load ? sharedConfig.load(key) // hydrating
1115
+ : fn(...args);
1116
+
1117
+ // serialize on server
1118
+ if (isServer && sharedConfig.context && !sharedConfig.context.noHydrate) {
1119
+ sharedConfig.context && sharedConfig.context.serialize(key, res);
1120
+ }
1121
+ if (intent !== "preload") {
1122
+ res = "then" in res ? res.then(handleResponse) : handleResponse(res);
1123
+ }
1124
+ if (cached) {
1125
+ cached[0] = now;
1126
+ cached[1] = res;
1127
+ cached[2] = intent;
1128
+ version && cached[3].add(version);
1129
+ if (!isServer && intent === "navigate") {
1130
+ startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
1131
+ }
1132
+ } else cache.set(key, cached = [now, res, intent, new Set(version ? [version] : [])]);
1133
+ return res;
1134
+ function handleRedirect(response) {
1135
+ startTransition(() => {
1136
+ let url = response.headers.get(LocationHeader);
1137
+ if (url && url.startsWith("/")) {
1138
+ navigate(url, {
1139
+ replace: true
1140
+ });
1141
+ } else if (!isServer && url) {
1142
+ window.location.href = url;
1143
+ }
1144
+ });
1145
+ }
1146
+ function handleResponse(v) {
1147
+ if (v instanceof Response && redirectStatusCodes.has(v.status)) {
1148
+ if (navigate) isServer ? handleRedirect(v) : setTimeout(() => handleRedirect(v), 0);
1149
+ return;
1150
+ }
1151
+ if (isServer) return v;
1152
+ setStore(key, reconcile(v, options));
1153
+ return store[key];
1154
+ }
1155
+ };
1156
+ }
1157
+
1158
+ function useSubmissions(fn, filter) {
1159
+ const router = useRouter();
1160
+ const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.toString() && (!filter || filter(s.input))));
1161
+ return new Proxy([], {
1162
+ get(_, property) {
1163
+ if (property === $TRACK) return subs();
1164
+ if (property === "pending") return subs().some(sub => !sub.result);
1165
+ return subs()[property];
1166
+ }
1167
+ });
1168
+ }
1169
+ function useSubmission(fn, filter) {
1170
+ const submissions = useSubmissions(fn, filter);
1171
+ return {
1172
+ get clear() {
1173
+ return submissions[0]?.clear;
1174
+ },
1175
+ get retry() {
1176
+ return submissions[0]?.retry;
1177
+ },
1178
+ get url() {
1179
+ return submissions[0]?.url;
1180
+ },
1181
+ get input() {
1182
+ return submissions[0]?.input;
1183
+ },
1184
+ get result() {
1185
+ return submissions[0]?.result;
1186
+ },
1187
+ get pending() {
1188
+ return submissions[0]?.pending;
1189
+ }
1190
+ };
1191
+ }
1192
+ function action(fn, name) {
1193
+ function mutate(variables) {
1194
+ const p = fn(variables);
1195
+ const [result, setResult] = createSignal();
1196
+ let submission;
1197
+ const router = this;
1198
+ router.submissions[1](s => [...s, submission = {
1199
+ input: variables,
1200
+ url,
1201
+ get result() {
1202
+ return result()?.data;
1203
+ },
1204
+ get pending() {
1205
+ return !result();
1206
+ },
1207
+ clear() {
1208
+ router.submissions[1](v => v.filter(i => i.input !== variables));
1209
+ },
1210
+ retry() {
1211
+ setResult(undefined);
1212
+ const p = fn(variables);
1213
+ p.then(async data => {
1214
+ const keys = handleResponse(data, router.navigatorFactory());
1215
+ await revalidate(keys);
1216
+ data ? setResult({
1217
+ data
1218
+ }) : submission.clear();
1219
+ return data;
1220
+ }).catch(error => {
1221
+ setResult({
1222
+ data: error
1223
+ });
1224
+ });
1225
+ return p;
1226
+ }
1227
+ }]);
1228
+ p.then(async data => {
1229
+ const keys = handleResponse(data, router.navigatorFactory());
1230
+ await revalidate(keys);
1231
+ data ? setResult({
1232
+ data
1233
+ }) : submission.clear();
1234
+ return data;
1235
+ }).catch(error => {
1236
+ setResult({
1237
+ data: error
1238
+ });
1239
+ });
1240
+ return p;
1241
+ }
1242
+ const url = fn.url || `action:${name}` || !isServer ? `action:${fn.name}` : "";
1243
+ mutate.toString = () => {
1244
+ if (!url) throw new Error("Client Actions need explicit names if server rendered");
1245
+ return url;
1246
+ };
1247
+ if (!isServer) registerAction(url, mutate);
1248
+ return mutate;
1249
+ }
1250
+ function handleResponse(response, navigate) {
1251
+ if (response instanceof Response && redirectStatusCodes.has(response.status)) {
1252
+ const locationUrl = response.headers.get("Location") || "/";
1253
+ if (locationUrl.startsWith("http")) {
1254
+ window.location.href = locationUrl;
1255
+ } else {
1256
+ navigate(locationUrl);
1257
+ }
1258
+ }
1259
+ // return keys
1260
+ return;
1261
+ }
1262
+
1263
+ export { A, A as Link, A as NavLink, Navigate, Route, Router, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createIntegration, createMemoryHistory, hashIntegration, memoryIntegration, normalizeIntegration, pathIntegration, revalidate, staticIntegration, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };