@solidjs/router 0.16.0 → 0.17.0-next.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 (78) hide show
  1. package/README.md +85 -79
  2. package/dist/components.jsx +22 -16
  3. package/dist/data/action.d.ts +12 -10
  4. package/dist/data/action.js +98 -78
  5. package/dist/data/events.d.ts +8 -1
  6. package/dist/data/events.js +3 -3
  7. package/dist/data/index.d.ts +2 -3
  8. package/dist/data/index.js +2 -3
  9. package/dist/data/query.d.ts +1 -3
  10. package/dist/data/query.js +10 -16
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +212 -261
  13. package/dist/index.jsx +1 -1
  14. package/dist/lifecycle.js +1 -1
  15. package/dist/routers/HashRouter.js +1 -1
  16. package/dist/routers/MemoryRouter.js +1 -1
  17. package/dist/routers/Router.js +2 -2
  18. package/dist/routers/StaticRouter.js +1 -1
  19. package/dist/routers/components.d.ts +0 -4
  20. package/dist/routers/components.jsx +30 -19
  21. package/dist/routing.d.ts +4 -3
  22. package/dist/routing.js +71 -49
  23. package/dist/types.d.ts +3 -18
  24. package/dist/utils.d.ts +1 -0
  25. package/dist/utils.js +8 -0
  26. package/package.json +8 -6
  27. package/dist/data/createAsync.d.ts +0 -32
  28. package/dist/data/createAsync.js +0 -93
  29. package/dist/src/components.d.ts +0 -31
  30. package/dist/src/components.jsx +0 -39
  31. package/dist/src/data/action.d.ts +0 -17
  32. package/dist/src/data/action.js +0 -163
  33. package/dist/src/data/action.spec.d.ts +0 -1
  34. package/dist/src/data/action.spec.js +0 -297
  35. package/dist/src/data/createAsync.d.ts +0 -32
  36. package/dist/src/data/createAsync.js +0 -96
  37. package/dist/src/data/createAsync.spec.d.ts +0 -1
  38. package/dist/src/data/createAsync.spec.js +0 -196
  39. package/dist/src/data/events.d.ts +0 -9
  40. package/dist/src/data/events.js +0 -123
  41. package/dist/src/data/events.spec.d.ts +0 -1
  42. package/dist/src/data/events.spec.js +0 -567
  43. package/dist/src/data/index.d.ts +0 -4
  44. package/dist/src/data/index.js +0 -4
  45. package/dist/src/data/query.d.ts +0 -23
  46. package/dist/src/data/query.js +0 -232
  47. package/dist/src/data/query.spec.d.ts +0 -1
  48. package/dist/src/data/query.spec.js +0 -354
  49. package/dist/src/data/response.d.ts +0 -4
  50. package/dist/src/data/response.js +0 -42
  51. package/dist/src/data/response.spec.d.ts +0 -1
  52. package/dist/src/data/response.spec.js +0 -165
  53. package/dist/src/index.d.ts +0 -7
  54. package/dist/src/index.jsx +0 -6
  55. package/dist/src/lifecycle.d.ts +0 -5
  56. package/dist/src/lifecycle.js +0 -69
  57. package/dist/src/routers/HashRouter.d.ts +0 -9
  58. package/dist/src/routers/HashRouter.js +0 -41
  59. package/dist/src/routers/MemoryRouter.d.ts +0 -24
  60. package/dist/src/routers/MemoryRouter.js +0 -57
  61. package/dist/src/routers/Router.d.ts +0 -9
  62. package/dist/src/routers/Router.js +0 -45
  63. package/dist/src/routers/StaticRouter.d.ts +0 -6
  64. package/dist/src/routers/StaticRouter.js +0 -15
  65. package/dist/src/routers/components.d.ts +0 -27
  66. package/dist/src/routers/components.jsx +0 -118
  67. package/dist/src/routers/createRouter.d.ts +0 -10
  68. package/dist/src/routers/createRouter.js +0 -41
  69. package/dist/src/routers/index.d.ts +0 -11
  70. package/dist/src/routers/index.js +0 -6
  71. package/dist/src/routing.d.ts +0 -175
  72. package/dist/src/routing.js +0 -560
  73. package/dist/src/types.d.ts +0 -200
  74. package/dist/src/types.js +0 -1
  75. package/dist/src/utils.d.ts +0 -13
  76. package/dist/src/utils.js +0 -185
  77. package/dist/test/helpers.d.ts +0 -6
  78. package/dist/test/helpers.js +0 -50
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
- import { isServer, getRequestEvent, createComponent as createComponent$1, memo, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
2
- import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, batch, createComponent, children, mergeProps, Show, createRoot, sharedConfig, getListener, $TRACK, splitProps, createResource, catchError } from 'solid-js';
3
- import { createStore, reconcile, unwrap } from 'solid-js/store';
1
+ import { isServer, getRequestEvent, createComponent as createComponent$1, memo, delegateEvents, spread, mergeProps, template } from '@solidjs/web';
2
+ import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, flush, createComponent, children, merge, createRoot, sharedConfig, getObserver, $TRACK, action as action$1, omit } from 'solid-js';
4
3
 
5
4
  function createBeforeLeave() {
6
5
  let listeners = new Set();
@@ -251,8 +250,15 @@ const MAX_REDIRECTS = 100;
251
250
  /** Consider this API opaque and internal. It is likely to change in the future. */
252
251
  const RouterContextObj = createContext();
253
252
  const RouteContextObj = createContext();
253
+ function useOptionalContext(context) {
254
+ try {
255
+ return useContext(context);
256
+ } catch {
257
+ return undefined;
258
+ }
259
+ }
254
260
  const useRouter = () => invariant(useContext(RouterContextObj), "<A> and 'use' router primitives can be only used inside a Route.");
255
- const useRoute = () => useContext(RouteContextObj) || useRouter().base;
261
+ const useRoute = () => useOptionalContext(RouteContextObj) || useRouter().base;
256
262
  const useResolvedPath = path => {
257
263
  const route = useRoute();
258
264
  return createMemo(() => route.resolvePath(path()));
@@ -299,8 +305,8 @@ const useNavigate = () => useRouter().navigatorFactory();
299
305
  const useLocation = () => useRouter().location;
300
306
 
301
307
  /**
302
- * Retrieves signal that indicates whether the route is currently in a *Transition*.
303
- * Useful for showing stale/pending state when the route resolution is *Suspended* during concurrent rendering.
308
+ * Retrieves a signal that indicates whether the router is currently processing a navigation.
309
+ * Useful for showing pending navigation state while the next route and its data settle.
304
310
  *
305
311
  * @example
306
312
  * ```js
@@ -459,7 +465,6 @@ function createRoutes(routeDef, base = "") {
459
465
  const {
460
466
  component,
461
467
  preload,
462
- load,
463
468
  children,
464
469
  info
465
470
  } = routeDef;
@@ -467,7 +472,7 @@ function createRoutes(routeDef, base = "") {
467
472
  const shared = {
468
473
  key: routeDef,
469
474
  component,
470
- preload: preload || load,
475
+ preload,
471
476
  info
472
477
  };
473
478
  return asArray(routeDef.path).reduce((acc, originalPath) => {
@@ -561,7 +566,7 @@ function createLocation(path, state, queryWrapper) {
561
566
  const search = createMemo(() => url().search, true);
562
567
  const hash = createMemo(() => url().hash);
563
568
  const key = () => "";
564
- const queryFn = on(search, () => extractSearchParams(url()));
569
+ const queryFn = createMemo(() => extractSearchParams(url()));
565
570
  return {
566
571
  get pathname() {
567
572
  return pathname();
@@ -610,40 +615,35 @@ function createRouterContext(integration, branches, getContext, options = {}) {
610
615
  scroll: false
611
616
  });
612
617
  }
613
- const [isRouting, setIsRouting] = createSignal(false);
618
+ const [isRouting, setIsRouting] = createSignal(false, {
619
+ pureWrite: true
620
+ });
614
621
 
615
- // Keep track of last target, so that last call to transition wins
616
- let lastTransitionTarget;
622
+ // Navigate override written from event handlers.
623
+ const [navigateTarget, setNavigateTarget] = createSignal(undefined, {
624
+ pureWrite: true
625
+ });
617
626
 
618
- // Transition the location to a new value
619
- const transition = (newIntent, newTarget) => {
620
- if (newTarget.value === reference() && newTarget.state === state()) return;
621
- if (lastTransitionTarget === undefined) setIsRouting(true);
622
- intent = newIntent;
623
- lastTransitionTarget = newTarget;
624
- startTransition(() => {
625
- if (lastTransitionTarget !== newTarget) return;
626
- setReference(lastTransitionTarget.value);
627
- setState(lastTransitionTarget.state);
628
- resetErrorBoundaries();
629
- if (!isServer) submissions[1](subs => subs.filter(s => s.pending));
630
- }).finally(() => {
631
- if (lastTransitionTarget !== newTarget) return;
627
+ // Keep track of last target, so that last call to navigate wins
628
+ let lastTransitionTarget;
632
629
 
633
- // Batch, in order for isRouting and final source update to happen together
634
- batch(() => {
635
- intent = undefined;
636
- if (newIntent === "navigate") navigateEnd(lastTransitionTarget);
637
- setIsRouting(false);
638
- lastTransitionTarget = undefined;
639
- });
640
- });
641
- };
642
- const [reference, setReference] = createSignal(source().value);
643
- const [state, setState] = createSignal(source().state);
630
+ // source() remains canonical for native history changes; navigateTarget()
631
+ // temporarily overrides it for in-flight programmatic navigation.
632
+ const reference = createMemo(() => {
633
+ const nav = navigateTarget();
634
+ if (nav !== undefined) return nav.value;
635
+ return source().value;
636
+ });
637
+ const state = createMemo(() => {
638
+ const nav = navigateTarget();
639
+ if (nav !== undefined) return nav.state;
640
+ return source().state;
641
+ });
644
642
  const location = createLocation(reference, state, utils.queryWrapper);
645
643
  const referrers = [];
646
- const submissions = createSignal(isServer ? initFromFlash() : []);
644
+ const submissions = createSignal(isServer ? initFromFlash() : [], {
645
+ pureWrite: true
646
+ });
647
647
  const matches = createMemo(() => {
648
648
  if (typeof options.transformUrl === "function") {
649
649
  return getRouteMatches(branches(), options.transformUrl(location.pathname));
@@ -667,11 +667,6 @@ function createRouterContext(integration, branches, getContext, options = {}) {
667
667
  return resolvePath(basePath, to);
668
668
  }
669
669
  };
670
-
671
- // Create a native transition, when source updates
672
- createRenderEffect(on(source, source => transition("native", source), {
673
- defer: true
674
- }));
675
670
  return {
676
671
  base: baseRoute,
677
672
  location,
@@ -738,17 +733,36 @@ function createRouterContext(integration, branches, getContext, options = {}) {
738
733
  scroll,
739
734
  state: state()
740
735
  });
741
- transition("navigate", {
736
+ const newTarget = {
742
737
  value: resolvedTo,
743
738
  state: nextState
744
- });
739
+ };
740
+ if (lastTransitionTarget === undefined) {
741
+ setIsRouting(true);
742
+ flush();
743
+ }
744
+ intent = "navigate";
745
+ lastTransitionTarget = newTarget;
746
+ if (lastTransitionTarget === newTarget) {
747
+ setNavigateTarget({
748
+ ...lastTransitionTarget
749
+ });
750
+ queueMicrotask(() => {
751
+ if (lastTransitionTarget !== newTarget) return;
752
+ intent = undefined;
753
+ navigateEnd(lastTransitionTarget);
754
+ setNavigateTarget(undefined);
755
+ setIsRouting(false);
756
+ lastTransitionTarget = undefined;
757
+ });
758
+ }
745
759
  }
746
760
  }
747
761
  });
748
762
  }
749
763
  function navigatorFactory(route) {
750
764
  // Workaround for vite issue (https://github.com/vitejs/vite/issues/3803)
751
- route = route || useContext(RouteContextObj) || baseRoute;
765
+ route = route || useOptionalContext(RouteContextObj) || baseRoute;
752
766
  return (to, options) => navigateFromRoute(route, to, options);
753
767
  }
754
768
  function navigateEnd(next) {
@@ -794,7 +808,12 @@ function createRouterContext(integration, branches, getContext, options = {}) {
794
808
  }
795
809
  function initFromFlash() {
796
810
  const e = getRequestEvent();
797
- return e && e.router && e.router.submission ? [e.router.submission] : [];
811
+ if (!(e && e.router && e.router.submission)) return [];
812
+ return [{
813
+ ...e.router.submission,
814
+ clear() {},
815
+ retry() {}
816
+ }];
798
817
  }
799
818
  }
800
819
  function createRouteContext(router, parent, outlet, match) {
@@ -849,7 +868,7 @@ const createRouterComponent = router => props => {
849
868
  transformUrl: props.transformUrl
850
869
  });
851
870
  router.create && router.create(routerState);
852
- return createComponent$1(RouterContextObj.Provider, {
871
+ return createComponent$1(RouterContextObj, {
853
872
  value: routerState,
854
873
  get children() {
855
874
  return createComponent$1(Root, {
@@ -858,7 +877,7 @@ const createRouterComponent = router => props => {
858
877
  return props.root;
859
878
  },
860
879
  get preload() {
861
- return props.rootPreload || props.rootLoad;
880
+ return props.rootPreload;
862
881
  },
863
882
  get children() {
864
883
  return [memo(() => (context = getOwner()) && null), createComponent$1(Routes, {
@@ -884,15 +903,9 @@ function Root(props) {
884
903
  });
885
904
  setInPreloadFn(false);
886
905
  }));
887
- return createComponent$1(Show, {
888
- get when() {
889
- return props.root;
890
- },
891
- keyed: true,
892
- get fallback() {
893
- return props.children;
894
- },
895
- children: Root => createComponent$1(Root, {
906
+ const RootComp = props.root;
907
+ if (RootComp) {
908
+ return createComponent$1(RootComp, {
896
909
  params: params,
897
910
  location: location,
898
911
  get data() {
@@ -901,8 +914,9 @@ function Root(props) {
901
914
  get children() {
902
915
  return props.children;
903
916
  }
904
- })
905
- });
917
+ });
918
+ }
919
+ return props.children;
906
920
  }
907
921
  function Routes(props) {
908
922
  if (isServer) {
@@ -925,11 +939,14 @@ function Routes(props) {
925
939
  }
926
940
  const disposers = [];
927
941
  let root;
928
- const routeStates = createMemo(on(props.routerState.matches, (nextMatches, prevMatches, prev) => {
929
- let equal = prevMatches && nextMatches.length === prevMatches.length;
942
+ let prevMatches;
943
+ const routeStates = createMemo(prev => {
944
+ const nextMatches = props.routerState.matches();
945
+ const previousMatches = prevMatches;
946
+ let equal = previousMatches && nextMatches.length === previousMatches.length;
930
947
  const next = [];
931
948
  for (let i = 0, len = nextMatches.length; i < len; i++) {
932
- const prevMatch = prevMatches && prevMatches[i];
949
+ const prevMatch = previousMatches && previousMatches[i];
933
950
  const nextMatch = nextMatches[i];
934
951
  if (prev && prevMatch && nextMatch.route.key === prevMatch.route.key) {
935
952
  next[i] = prev[i];
@@ -940,7 +957,7 @@ function Routes(props) {
940
957
  }
941
958
  createRoot(dispose => {
942
959
  disposers[i] = dispose;
943
- next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
960
+ next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()?.[i + 1]), () => {
944
961
  const routeMatches = props.routerState.matches();
945
962
  return routeMatches[i] ?? routeMatches[0];
946
963
  });
@@ -949,30 +966,32 @@ function Routes(props) {
949
966
  }
950
967
  disposers.splice(nextMatches.length).forEach(dispose => dispose());
951
968
  if (prev && equal) {
969
+ prevMatches = nextMatches;
952
970
  return prev;
953
971
  }
954
972
  root = next[0];
973
+ prevMatches = nextMatches;
955
974
  return next;
956
- }));
975
+ }, undefined);
957
976
  return createOutlet(() => routeStates() && root)();
958
977
  }
959
978
  const createOutlet = child => {
960
- return () => createComponent$1(Show, {
961
- get when() {
962
- return child();
963
- },
964
- keyed: true,
965
- children: child => createComponent$1(RouteContextObj.Provider, {
966
- value: child,
967
- get children() {
968
- return child.outlet();
969
- }
970
- })
971
- });
979
+ return () => {
980
+ const c = child();
981
+ if (c) {
982
+ return createComponent$1(RouteContextObj, {
983
+ value: c,
984
+ get children() {
985
+ return c.outlet();
986
+ }
987
+ });
988
+ }
989
+ return undefined;
990
+ };
972
991
  };
973
992
  const Route = props => {
974
993
  const childRoutes = children(() => props.children);
975
- return mergeProps(props, {
994
+ return merge(props, {
976
995
  get children() {
977
996
  return childRoutes();
978
997
  }
@@ -1078,12 +1097,10 @@ function getCache() {
1078
1097
  * Revalidates the given cache entry/entries.
1079
1098
  */
1080
1099
  function revalidate(key, force = true) {
1081
- return startTransition(() => {
1082
- const now = Date.now();
1083
- cacheKeyOp(key, entry => {
1084
- force && (entry[0] = 0); //force cache miss
1085
- entry[4][1](now); // retrigger live signals
1086
- });
1100
+ const now = Date.now();
1101
+ cacheKeyOp(key, entry => {
1102
+ force && (entry[0] = 0); //force cache miss
1103
+ entry[4][1](now); // retrigger live signals
1087
1104
  });
1088
1105
  }
1089
1106
  function cacheKeyOp(key, fn) {
@@ -1119,7 +1136,7 @@ function query(fn, name) {
1119
1136
  }
1120
1137
  }
1121
1138
  }
1122
- if (getListener() && !isServer) {
1139
+ if (getObserver() && !isServer) {
1123
1140
  tracking = true;
1124
1141
  onCleanup(() => cached[4].count--);
1125
1142
  }
@@ -1134,7 +1151,7 @@ function query(fn, name) {
1134
1151
  let res = cached[1];
1135
1152
  if (intent !== "preload") {
1136
1153
  res = "then" in cached[1] ? cached[1].then(handleResponse(false), handleResponse(true)) : handleResponse(false)(cached[1]);
1137
- !isServer && intent === "navigate" && startTransition(() => cached[4][1](cached[0])); // update version
1154
+ !isServer && intent === "navigate" && cached[4][1](cached[0]); // update version
1138
1155
  }
1139
1156
  inPreloadFn && "then" in res && res.catch(() => {});
1140
1157
  return res;
@@ -1149,7 +1166,7 @@ function query(fn, name) {
1149
1166
  cached[0] = now;
1150
1167
  cached[1] = res;
1151
1168
  cached[3] = intent;
1152
- !isServer && intent === "navigate" && startTransition(() => cached[4][1](cached[0])); // update version
1169
+ !isServer && intent === "navigate" && cached[4][1](cached[0]); // update version
1153
1170
  } else {
1154
1171
  cache.set(key, cached = [now, res,, intent, createSignal(now)]);
1155
1172
  cached[4].count = 0;
@@ -1184,10 +1201,8 @@ function query(fn, name) {
1184
1201
  const url = v.headers.get(LocationHeader);
1185
1202
  if (url !== null) {
1186
1203
  // client + server relative redirect
1187
- if (navigate && url.startsWith("/")) startTransition(() => {
1188
- navigate(url, {
1189
- replace: true
1190
- });
1204
+ if (navigate && url.startsWith("/")) navigate(url, {
1205
+ replace: true
1191
1206
  });else if (!isServer) window.location.href = url;else if (e) e.response.status = 302;
1192
1207
  return;
1193
1208
  }
@@ -1223,9 +1238,6 @@ query.set = (key, value) => {
1223
1238
  };
1224
1239
  query.delete = key => getCache().delete(key);
1225
1240
  query.clear = () => getCache().clear();
1226
-
1227
- /** @deprecated use query instead */
1228
- const cache = query;
1229
1241
  function matchKey(key, keys) {
1230
1242
  for (let k of keys) {
1231
1243
  if (k && key.startsWith(k)) return true;
@@ -1246,6 +1258,9 @@ function isPlainObject(obj) {
1246
1258
  return obj != null && typeof obj === "object" && (!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype);
1247
1259
  }
1248
1260
 
1261
+ const submitHooksSymbol = Symbol("routerActionSubmitHooks");
1262
+ const settledHooksSymbol = Symbol("routerActionSettledHooks");
1263
+ const invokeSymbol = Symbol("routerActionInvoke");
1249
1264
  const actions = /* #__PURE__ */new Map();
1250
1265
  function useSubmissions(fn, filter) {
1251
1266
  const router = useRouter();
@@ -1253,7 +1268,6 @@ function useSubmissions(fn, filter) {
1253
1268
  return new Proxy([], {
1254
1269
  get(_, property) {
1255
1270
  if (property === $TRACK) return subs();
1256
- if (property === "pending") return subs().some(sub => !sub.result);
1257
1271
  return subs()[property];
1258
1272
  },
1259
1273
  has(_, property) {
@@ -1261,99 +1275,111 @@ function useSubmissions(fn, filter) {
1261
1275
  }
1262
1276
  });
1263
1277
  }
1264
- function useSubmission(fn, filter) {
1265
- const submissions = useSubmissions(fn, filter);
1266
- return new Proxy({}, {
1267
- get(_, property) {
1268
- if (submissions.length === 0 && property === "clear" || property === "retry") return () => {};
1269
- return submissions[submissions.length - 1]?.[property];
1270
- }
1271
- });
1272
- }
1273
1278
  function useAction(action) {
1274
1279
  const r = useRouter();
1275
1280
  return (...args) => action.apply({
1276
1281
  r
1277
1282
  }, args);
1278
1283
  }
1279
- function action(fn, options = {}) {
1280
- function mutate(...variables) {
1284
+ function actionImpl(fn, options = {}) {
1285
+ async function invoke(variables, current) {
1281
1286
  const router = this.r;
1282
1287
  const form = this.f;
1283
- const p = (router.singleFlight && fn.withOptions ? fn.withOptions({
1288
+ const submitHooks = current[submitHooksSymbol];
1289
+ const settledHooks = current[settledHooksSymbol];
1290
+ const runMutation = () => (router.singleFlight && fn.withOptions ? fn.withOptions({
1284
1291
  headers: {
1285
1292
  "X-Single-Flight": "true"
1286
1293
  }
1287
1294
  }) : fn)(...variables);
1288
- const [result, setResult] = createSignal();
1295
+ const run = action$1(async function* (context) {
1296
+ context.optimistic?.();
1297
+ try {
1298
+ const value = await context.call();
1299
+ yield;
1300
+ return {
1301
+ error: false,
1302
+ value
1303
+ };
1304
+ } catch (error) {
1305
+ yield;
1306
+ return {
1307
+ error: true,
1308
+ value: error
1309
+ };
1310
+ }
1311
+ });
1312
+ const settled = await settleActionResult(run({
1313
+ call: runMutation,
1314
+ optimistic: submitHooks.size ? () => {
1315
+ for (const hook of submitHooks.values()) hook(...variables);
1316
+ } : undefined
1317
+ }));
1318
+ const response = await handleResponse(settled.value, settled.error, router.navigatorFactory());
1319
+ if (!response) return undefined;
1289
1320
  let submission;
1290
- function handler(error) {
1291
- return async res => {
1292
- const result = await handleResponse(res, error, router.navigatorFactory());
1293
- let retry = null;
1294
- o.onComplete?.({
1295
- ...submission,
1296
- result: result?.data,
1297
- error: result?.error,
1298
- pending: false,
1299
- retry() {
1300
- return retry = submission.retry();
1301
- }
1302
- });
1303
- if (retry) return retry;
1304
- if (!result) return submission.clear();
1305
- setResult(result);
1306
- if (result.error && !form) throw result.error;
1307
- return result.data;
1308
- };
1309
- }
1310
- router.submissions[1](s => [...s, submission = {
1321
+ submission = {
1311
1322
  input: variables,
1312
1323
  url,
1313
- get result() {
1314
- return result()?.data;
1315
- },
1316
- get error() {
1317
- return result()?.error;
1318
- },
1319
- get pending() {
1320
- return !result();
1321
- },
1324
+ result: response.data,
1325
+ error: response.error,
1322
1326
  clear() {
1323
- router.submissions[1](v => v.filter(i => i !== submission));
1327
+ router.submissions[1](entries => entries.filter(entry => entry !== submission));
1324
1328
  },
1325
1329
  retry() {
1326
- setResult(undefined);
1327
- const p = fn(...variables);
1328
- return p.then(handler(), handler(true));
1330
+ submission.clear();
1331
+ return current[invokeSymbol].call({
1332
+ r: router,
1333
+ f: form
1334
+ }, variables, current);
1329
1335
  }
1330
- }]);
1331
- return p.then(handler(), handler(true));
1336
+ };
1337
+ router.submissions[1](entries => [...entries, submission]);
1338
+ for (const hook of settledHooks.values()) hook(submission);
1339
+ if (response.error && !form) throw response.error;
1340
+ return response.data;
1332
1341
  }
1333
1342
  const o = typeof options === "string" ? {
1334
1343
  name: options
1335
1344
  } : options;
1336
1345
  const name = o.name || (!isServer ? String(hashString(fn.toString())) : undefined);
1337
1346
  const url = fn.url || name && `https://action/${name}` || "";
1338
- mutate.base = url;
1339
- if (name) setFunctionName(mutate, name);
1340
- return toAction(mutate, url);
1341
- }
1342
- function toAction(fn, url) {
1347
+ const wrapped = toAction(invoke, url);
1348
+ if (name) setFunctionName(wrapped, name);
1349
+ return wrapped;
1350
+ }
1351
+ const action = actionImpl;
1352
+ function toAction(invoke, url, boundArgs = [], base = url, submitHooks = new Map(), settledHooks = new Map()) {
1353
+ const fn = function (...args) {
1354
+ return invoke.call(this, [...boundArgs, ...args], fn);
1355
+ };
1343
1356
  fn.toString = () => {
1344
1357
  if (!url) throw new Error("Client Actions need explicit names if server rendered");
1345
1358
  return url;
1346
1359
  };
1347
1360
  fn.with = function (...args) {
1348
- const newFn = function (...passedArgs) {
1349
- return fn.call(this, ...args, ...passedArgs);
1350
- };
1351
- newFn.base = fn.base;
1352
1361
  const uri = new URL(url, mockBase);
1353
1362
  uri.searchParams.set("args", hashKey(args));
1354
- return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
1363
+ const next = toAction(invoke, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search, [...boundArgs, ...args], base, submitHooks, settledHooks);
1364
+ return next;
1365
+ };
1366
+ fn.onSubmit = function (hook) {
1367
+ const id = Symbol("actionOnSubmitHook");
1368
+ submitHooks.set(id, hook);
1369
+ getOwner() && onCleanup(() => submitHooks.delete(id));
1370
+ return this;
1371
+ };
1372
+ fn.onSettled = function (hook) {
1373
+ const id = Symbol("actionOnSettledHook");
1374
+ settledHooks.set(id, hook);
1375
+ getOwner() && onCleanup(() => settledHooks.delete(id));
1376
+ return this;
1355
1377
  };
1356
1378
  fn.url = url;
1379
+ fn.base = base;
1380
+ fn[submitHooksSymbol] = submitHooks;
1381
+ fn[settledHooksSymbol] = settledHooks;
1382
+ fn[invokeSymbol] = invoke;
1357
1383
  if (!isServer) {
1358
1384
  actions.set(url, fn);
1359
1385
  getOwner() && onCleanup(() => actions.delete(url));
@@ -1361,6 +1387,21 @@ function toAction(fn, url) {
1361
1387
  return fn;
1362
1388
  }
1363
1389
  const hashString = s => s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
1390
+ async function settleActionResult(result) {
1391
+ const value = result;
1392
+ if (value && typeof value.then === "function") {
1393
+ return result.then(value => value);
1394
+ }
1395
+ if (value && typeof value.next === "function") {
1396
+ const iterator = value;
1397
+ let next = await iterator.next();
1398
+ while (!next.done) {
1399
+ next = await iterator.next();
1400
+ }
1401
+ return next.value;
1402
+ }
1403
+ return result;
1404
+ }
1364
1405
  async function handleResponse(response, error, navigate) {
1365
1406
  let data;
1366
1407
  let custom;
@@ -1669,12 +1710,21 @@ function MemoryRouter(props) {
1669
1710
  }
1670
1711
 
1671
1712
  var _tmpl$ = /*#__PURE__*/template(`<a>`);
1713
+ function toClassName(value) {
1714
+ if (!value) return "";
1715
+ if (typeof value === "string" || typeof value === "number") return String(value);
1716
+ if (Array.isArray(value)) return value.map(toClassName).filter(Boolean).join(" ");
1717
+ if (typeof value === "object") {
1718
+ return Object.entries(value).filter(([, enabled]) => enabled).map(([name]) => name).join(" ");
1719
+ }
1720
+ return "";
1721
+ }
1672
1722
  function A(props) {
1673
- props = mergeProps({
1723
+ props = merge({
1674
1724
  inactiveClass: "inactive",
1675
1725
  activeClass: "active"
1676
1726
  }, props);
1677
- const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
1727
+ const rest = omit(props, "href", "state", "class", "activeClass", "inactiveClass", "end");
1678
1728
  const to = useResolvedPath(() => props.href);
1679
1729
  const href = useHref(to);
1680
1730
  const location = useLocation();
@@ -1685,26 +1735,20 @@ function A(props) {
1685
1735
  const loc = decodeURI(normalizePath(location.pathname).toLowerCase());
1686
1736
  return [props.end ? path === loc : loc.startsWith(path + "/") || loc === path, path === loc];
1687
1737
  });
1738
+ const className = createMemo(() => [toClassName(props.class), isActive()[0] ? props.activeClass : props.inactiveClass].filter(Boolean).join(" "));
1688
1739
  return (() => {
1689
1740
  var _el$ = _tmpl$();
1690
- spread(_el$, mergeProps$1(rest, {
1741
+ spread(_el$, mergeProps(rest, {
1691
1742
  get href() {
1692
1743
  return href() || props.href;
1693
1744
  },
1694
1745
  get state() {
1695
1746
  return JSON.stringify(props.state);
1696
1747
  },
1697
- get classList() {
1698
- return {
1699
- ...(props.class && {
1700
- [props.class]: true
1701
- }),
1702
- [props.inactiveClass]: !isActive()[0],
1703
- [props.activeClass]: isActive()[0],
1704
- ...rest.classList
1705
- };
1748
+ get ["class"]() {
1749
+ return className();
1706
1750
  },
1707
- "link": "",
1751
+ "link": true,
1708
1752
  get ["aria-current"]() {
1709
1753
  return isActive()[1] ? "page" : undefined;
1710
1754
  }
@@ -1730,99 +1774,6 @@ function Navigate(props) {
1730
1774
  return null;
1731
1775
  }
1732
1776
 
1733
- /**
1734
- * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
1735
- */
1736
-
1737
- /**
1738
- * As `createAsync` and `createAsyncStore` are wrappers for `createResource`,
1739
- * this type allows to support `latest` field for these primitives.
1740
- * It will be removed in the future.
1741
- */
1742
-
1743
- function createAsync(fn, options) {
1744
- let resource;
1745
- let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
1746
- [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, options);
1747
- const resultAccessor = () => resource();
1748
- if (options?.name) setFunctionName(resultAccessor, options.name);
1749
- Object.defineProperty(resultAccessor, "latest", {
1750
- get() {
1751
- return resource.latest;
1752
- }
1753
- });
1754
- return resultAccessor;
1755
- }
1756
- function createAsyncStore(fn, options = {}) {
1757
- let resource;
1758
- let prev = () => !resource || resource.state === "unresolved" ? undefined : unwrap(resource.latest);
1759
- [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, {
1760
- ...options,
1761
- storage: init => createDeepSignal(init, options.reconcile)
1762
- });
1763
- const resultAccessor = () => resource();
1764
- Object.defineProperty(resultAccessor, "latest", {
1765
- get() {
1766
- return resource.latest;
1767
- }
1768
- });
1769
- return resultAccessor;
1770
- }
1771
- function createDeepSignal(value, options) {
1772
- const [store, setStore] = createStore({
1773
- value: structuredClone(value)
1774
- });
1775
- return [() => store.value, v => {
1776
- typeof v === "function" && (v = v());
1777
- setStore("value", reconcile(structuredClone(v), options));
1778
- return store.value;
1779
- }];
1780
- }
1781
-
1782
- // mock promise while hydrating to prevent fetching
1783
- class MockPromise {
1784
- static all() {
1785
- return new MockPromise();
1786
- }
1787
- static allSettled() {
1788
- return new MockPromise();
1789
- }
1790
- static any() {
1791
- return new MockPromise();
1792
- }
1793
- static race() {
1794
- return new MockPromise();
1795
- }
1796
- static reject() {
1797
- return new MockPromise();
1798
- }
1799
- static resolve() {
1800
- return new MockPromise();
1801
- }
1802
- catch() {
1803
- return new MockPromise();
1804
- }
1805
- then() {
1806
- return new MockPromise();
1807
- }
1808
- finally() {
1809
- return new MockPromise();
1810
- }
1811
- }
1812
- function subFetch(fn, prev) {
1813
- if (isServer || !sharedConfig.context) return fn(prev);
1814
- const ogFetch = fetch;
1815
- const ogPromise = Promise;
1816
- try {
1817
- window.fetch = () => new MockPromise();
1818
- Promise = MockPromise;
1819
- return fn(prev);
1820
- } finally {
1821
- window.fetch = ogFetch;
1822
- Promise = ogPromise;
1823
- }
1824
- }
1825
-
1826
1777
  function redirect(url, init = 302) {
1827
1778
  let responseInit;
1828
1779
  let revalidate;
@@ -1876,4 +1827,4 @@ function json(data, init = {}) {
1876
1827
  return response;
1877
1828
  }
1878
1829
 
1879
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, RouterContextObj as RouterContext, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, query, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, usePreloadRoute, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
1830
+ export { A, HashRouter, MemoryRouter, Navigate, Route, Router, RouterContextObj as RouterContext, StaticRouter, mergeSearchString as _mergeSearchString, action, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, query, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, usePreloadRoute, useResolvedPath, useSearchParams, useSubmissions };