@solidjs/router 0.16.1 → 0.17.0-next.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,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();
@@ -601,49 +606,45 @@ function createRouterContext(integration, branches, getContext, options = {}) {
601
606
  const renderPath = utils.renderPath || (p => p);
602
607
  const beforeLeave = utils.beforeLeave || createBeforeLeave();
603
608
  const basePath = resolvePath("", options.base || "");
609
+ const initialSource = untrack(source);
604
610
  if (basePath === undefined) {
605
611
  throw new Error(`${basePath} is not a valid base path`);
606
- } else if (basePath && !source().value) {
612
+ } else if (basePath && !initialSource.value) {
607
613
  setSource({
608
614
  value: basePath,
609
615
  replace: true,
610
616
  scroll: false
611
617
  });
612
618
  }
613
- const [isRouting, setIsRouting] = createSignal(false);
619
+ const [isRouting, setIsRouting] = createSignal(false, {
620
+ pureWrite: true
621
+ });
614
622
 
615
- // Keep track of last target, so that last call to transition wins
616
- let lastTransitionTarget;
623
+ // Navigate override written from event handlers.
624
+ const [navigateTarget, setNavigateTarget] = createSignal(undefined, {
625
+ pureWrite: true
626
+ });
617
627
 
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;
628
+ // Keep track of last target, so that last call to navigate wins
629
+ let lastTransitionTarget;
632
630
 
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);
631
+ // source() remains canonical for native history changes; navigateTarget()
632
+ // temporarily overrides it for in-flight programmatic navigation.
633
+ const reference = createMemo(() => {
634
+ const nav = navigateTarget();
635
+ if (nav !== undefined) return nav.value;
636
+ return source().value;
637
+ });
638
+ const state = createMemo(() => {
639
+ const nav = navigateTarget();
640
+ if (nav !== undefined) return nav.state;
641
+ return source().state;
642
+ });
644
643
  const location = createLocation(reference, state, utils.queryWrapper);
645
644
  const referrers = [];
646
- const submissions = createSignal(isServer ? initFromFlash() : []);
645
+ const submissions = createSignal(isServer ? initFromFlash() : [], {
646
+ pureWrite: true
647
+ });
647
648
  const matches = createMemo(() => {
648
649
  if (typeof options.transformUrl === "function") {
649
650
  return getRouteMatches(branches(), options.transformUrl(location.pathname));
@@ -667,11 +668,6 @@ function createRouterContext(integration, branches, getContext, options = {}) {
667
668
  return resolvePath(basePath, to);
668
669
  }
669
670
  };
670
-
671
- // Create a native transition, when source updates
672
- createRenderEffect(on(source, source => transition("native", source), {
673
- defer: true
674
- }));
675
671
  return {
676
672
  base: baseRoute,
677
673
  location,
@@ -738,17 +734,36 @@ function createRouterContext(integration, branches, getContext, options = {}) {
738
734
  scroll,
739
735
  state: state()
740
736
  });
741
- transition("navigate", {
737
+ const newTarget = {
742
738
  value: resolvedTo,
743
739
  state: nextState
744
- });
740
+ };
741
+ if (lastTransitionTarget === undefined) {
742
+ setIsRouting(true);
743
+ flush();
744
+ }
745
+ intent = "navigate";
746
+ lastTransitionTarget = newTarget;
747
+ if (lastTransitionTarget === newTarget) {
748
+ setNavigateTarget({
749
+ ...lastTransitionTarget
750
+ });
751
+ queueMicrotask(() => {
752
+ if (lastTransitionTarget !== newTarget) return;
753
+ intent = undefined;
754
+ navigateEnd(lastTransitionTarget);
755
+ setNavigateTarget(undefined);
756
+ setIsRouting(false);
757
+ lastTransitionTarget = undefined;
758
+ });
759
+ }
745
760
  }
746
761
  }
747
762
  });
748
763
  }
749
764
  function navigatorFactory(route) {
750
765
  // Workaround for vite issue (https://github.com/vitejs/vite/issues/3803)
751
- route = route || useContext(RouteContextObj) || baseRoute;
766
+ route = route || useOptionalContext(RouteContextObj) || baseRoute;
752
767
  return (to, options) => navigateFromRoute(route, to, options);
753
768
  }
754
769
  function navigateEnd(next) {
@@ -794,7 +809,12 @@ function createRouterContext(integration, branches, getContext, options = {}) {
794
809
  }
795
810
  function initFromFlash() {
796
811
  const e = getRequestEvent();
797
- return e && e.router && e.router.submission ? [e.router.submission] : [];
812
+ if (!(e && e.router && e.router.submission)) return [];
813
+ return [{
814
+ ...e.router.submission,
815
+ clear() {},
816
+ retry() {}
817
+ }];
798
818
  }
799
819
  }
800
820
  function createRouteContext(router, parent, outlet, match) {
@@ -836,30 +856,38 @@ function createRouteContext(router, parent, outlet, match) {
836
856
  return route;
837
857
  }
838
858
 
839
- const createRouterComponent = router => props => {
859
+ const createRouterComponent = router => function IntegratedRouter(props) {
840
860
  const {
841
- base
842
- } = props;
843
- const routeDefs = children(() => props.children);
844
- const branches = createMemo(() => createBranches(routeDefs(), props.base || ""));
861
+ base,
862
+ singleFlight,
863
+ transformUrl,
864
+ root,
865
+ rootPreload,
866
+ routeChildren
867
+ } = untrack(() => ({
868
+ base: props.base,
869
+ singleFlight: props.singleFlight,
870
+ transformUrl: props.transformUrl,
871
+ root: props.root,
872
+ rootPreload: props.rootPreload,
873
+ routeChildren: props.children
874
+ }));
875
+ const routeDefs = children(() => routeChildren);
876
+ const branches = createMemo(() => createBranches(routeDefs(), base || ""));
845
877
  let context;
846
878
  const routerState = createRouterContext(router, branches, () => context, {
847
879
  base,
848
- singleFlight: props.singleFlight,
849
- transformUrl: props.transformUrl
880
+ singleFlight,
881
+ transformUrl
850
882
  });
851
883
  router.create && router.create(routerState);
852
- return createComponent$1(RouterContextObj.Provider, {
884
+ return createComponent$1(RouterContextObj, {
853
885
  value: routerState,
854
886
  get children() {
855
887
  return createComponent$1(Root, {
856
888
  routerState: routerState,
857
- get root() {
858
- return props.root;
859
- },
860
- get preload() {
861
- return props.rootPreload || props.rootLoad;
862
- },
889
+ root: root,
890
+ preload: rootPreload,
863
891
  get children() {
864
892
  return [memo(() => (context = getOwner()) && null), createComponent$1(Routes, {
865
893
  routerState: routerState,
@@ -884,15 +912,9 @@ function Root(props) {
884
912
  });
885
913
  setInPreloadFn(false);
886
914
  }));
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, {
915
+ const RootComp = props.root;
916
+ if (RootComp) {
917
+ return createComponent$1(RootComp, {
896
918
  params: params,
897
919
  location: location,
898
920
  get data() {
@@ -901,8 +923,9 @@ function Root(props) {
901
923
  get children() {
902
924
  return props.children;
903
925
  }
904
- })
905
- });
926
+ });
927
+ }
928
+ return props.children;
906
929
  }
907
930
  function Routes(props) {
908
931
  if (isServer) {
@@ -925,11 +948,14 @@ function Routes(props) {
925
948
  }
926
949
  const disposers = [];
927
950
  let root;
928
- const routeStates = createMemo(on(props.routerState.matches, (nextMatches, prevMatches, prev) => {
929
- let equal = prevMatches && nextMatches.length === prevMatches.length;
951
+ let prevMatches;
952
+ const routeStates = createMemo(prev => {
953
+ const nextMatches = props.routerState.matches();
954
+ const previousMatches = prevMatches;
955
+ let equal = previousMatches && nextMatches.length === previousMatches.length;
930
956
  const next = [];
931
957
  for (let i = 0, len = nextMatches.length; i < len; i++) {
932
- const prevMatch = prevMatches && prevMatches[i];
958
+ const prevMatch = previousMatches && previousMatches[i];
933
959
  const nextMatch = nextMatches[i];
934
960
  if (prev && prevMatch && nextMatch.route.key === prevMatch.route.key) {
935
961
  next[i] = prev[i];
@@ -940,7 +966,7 @@ function Routes(props) {
940
966
  }
941
967
  createRoot(dispose => {
942
968
  disposers[i] = dispose;
943
- next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
969
+ next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()?.[i + 1]), () => {
944
970
  const routeMatches = props.routerState.matches();
945
971
  return routeMatches[i] ?? routeMatches[0];
946
972
  });
@@ -949,30 +975,33 @@ function Routes(props) {
949
975
  }
950
976
  disposers.splice(nextMatches.length).forEach(dispose => dispose());
951
977
  if (prev && equal) {
978
+ prevMatches = nextMatches;
952
979
  return prev;
953
980
  }
954
981
  root = next[0];
982
+ prevMatches = nextMatches;
955
983
  return next;
956
- }));
957
- return createOutlet(() => routeStates() && root)();
984
+ }, undefined);
985
+ const outlet = createOutlet(() => routeStates() && root);
986
+ return memo(outlet);
958
987
  }
959
988
  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
- });
989
+ return () => {
990
+ const c = child();
991
+ if (c) {
992
+ return createComponent$1(RouteContextObj, {
993
+ value: c,
994
+ get children() {
995
+ return c.outlet();
996
+ }
997
+ });
998
+ }
999
+ return undefined;
1000
+ };
972
1001
  };
973
1002
  const Route = props => {
974
1003
  const childRoutes = children(() => props.children);
975
- return mergeProps(props, {
1004
+ return merge(props, {
976
1005
  get children() {
977
1006
  return childRoutes();
978
1007
  }
@@ -1078,12 +1107,10 @@ function getCache() {
1078
1107
  * Revalidates the given cache entry/entries.
1079
1108
  */
1080
1109
  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
- });
1110
+ const now = Date.now();
1111
+ cacheKeyOp(key, entry => {
1112
+ force && (entry[0] = 0); //force cache miss
1113
+ entry[4][1](now); // retrigger live signals
1087
1114
  });
1088
1115
  }
1089
1116
  function cacheKeyOp(key, fn) {
@@ -1119,7 +1146,7 @@ function query(fn, name) {
1119
1146
  }
1120
1147
  }
1121
1148
  }
1122
- if (getListener() && !isServer) {
1149
+ if (getObserver() && !isServer) {
1123
1150
  tracking = true;
1124
1151
  onCleanup(() => cached[4].count--);
1125
1152
  }
@@ -1134,7 +1161,7 @@ function query(fn, name) {
1134
1161
  let res = cached[1];
1135
1162
  if (intent !== "preload") {
1136
1163
  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
1164
+ !isServer && intent === "navigate" && cached[4][1](cached[0]); // update version
1138
1165
  }
1139
1166
  inPreloadFn && "then" in res && res.catch(() => {});
1140
1167
  return res;
@@ -1149,7 +1176,7 @@ function query(fn, name) {
1149
1176
  cached[0] = now;
1150
1177
  cached[1] = res;
1151
1178
  cached[3] = intent;
1152
- !isServer && intent === "navigate" && startTransition(() => cached[4][1](cached[0])); // update version
1179
+ !isServer && intent === "navigate" && cached[4][1](cached[0]); // update version
1153
1180
  } else {
1154
1181
  cache.set(key, cached = [now, res,, intent, createSignal(now)]);
1155
1182
  cached[4].count = 0;
@@ -1184,10 +1211,8 @@ function query(fn, name) {
1184
1211
  const url = v.headers.get(LocationHeader);
1185
1212
  if (url !== null) {
1186
1213
  // client + server relative redirect
1187
- if (navigate && url.startsWith("/")) startTransition(() => {
1188
- navigate(url, {
1189
- replace: true
1190
- });
1214
+ if (navigate && url.startsWith("/")) navigate(url, {
1215
+ replace: true
1191
1216
  });else if (!isServer) window.location.href = url;else if (e) e.response.status = 302;
1192
1217
  return;
1193
1218
  }
@@ -1223,9 +1248,6 @@ query.set = (key, value) => {
1223
1248
  };
1224
1249
  query.delete = key => getCache().delete(key);
1225
1250
  query.clear = () => getCache().clear();
1226
-
1227
- /** @deprecated use query instead */
1228
- const cache = query;
1229
1251
  function matchKey(key, keys) {
1230
1252
  for (let k of keys) {
1231
1253
  if (k && key.startsWith(k)) return true;
@@ -1246,6 +1268,9 @@ function isPlainObject(obj) {
1246
1268
  return obj != null && typeof obj === "object" && (!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype);
1247
1269
  }
1248
1270
 
1271
+ const submitHooksSymbol = Symbol("routerActionSubmitHooks");
1272
+ const settledHooksSymbol = Symbol("routerActionSettledHooks");
1273
+ const invokeSymbol = Symbol("routerActionInvoke");
1249
1274
  const actions = /* #__PURE__ */new Map();
1250
1275
  function useSubmissions(fn, filter) {
1251
1276
  const router = useRouter();
@@ -1253,7 +1278,6 @@ function useSubmissions(fn, filter) {
1253
1278
  return new Proxy([], {
1254
1279
  get(_, property) {
1255
1280
  if (property === $TRACK) return subs();
1256
- if (property === "pending") return subs().some(sub => !sub.result);
1257
1281
  return subs()[property];
1258
1282
  },
1259
1283
  has(_, property) {
@@ -1261,99 +1285,111 @@ function useSubmissions(fn, filter) {
1261
1285
  }
1262
1286
  });
1263
1287
  }
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
1288
  function useAction(action) {
1274
1289
  const r = useRouter();
1275
1290
  return (...args) => action.apply({
1276
1291
  r
1277
1292
  }, args);
1278
1293
  }
1279
- function action(fn, options = {}) {
1280
- function mutate(...variables) {
1294
+ function actionImpl(fn, options = {}) {
1295
+ async function invoke(variables, current) {
1281
1296
  const router = this.r;
1282
1297
  const form = this.f;
1283
- const p = (router.singleFlight && fn.withOptions ? fn.withOptions({
1298
+ const submitHooks = current[submitHooksSymbol];
1299
+ const settledHooks = current[settledHooksSymbol];
1300
+ const runMutation = () => (router.singleFlight && fn.withOptions ? fn.withOptions({
1284
1301
  headers: {
1285
1302
  "X-Single-Flight": "true"
1286
1303
  }
1287
1304
  }) : fn)(...variables);
1288
- const [result, setResult] = createSignal();
1305
+ const run = action$1(async function* (context) {
1306
+ context.optimistic?.();
1307
+ try {
1308
+ const value = await context.call();
1309
+ yield;
1310
+ return {
1311
+ error: false,
1312
+ value
1313
+ };
1314
+ } catch (error) {
1315
+ yield;
1316
+ return {
1317
+ error: true,
1318
+ value: error
1319
+ };
1320
+ }
1321
+ });
1322
+ const settled = await settleActionResult(run({
1323
+ call: runMutation,
1324
+ optimistic: submitHooks.size ? () => {
1325
+ for (const hook of submitHooks.values()) hook(...variables);
1326
+ } : undefined
1327
+ }));
1328
+ const response = await handleResponse(settled.value, settled.error, router.navigatorFactory());
1329
+ if (!response) return undefined;
1289
1330
  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 = {
1331
+ submission = {
1311
1332
  input: variables,
1312
1333
  url,
1313
- get result() {
1314
- return result()?.data;
1315
- },
1316
- get error() {
1317
- return result()?.error;
1318
- },
1319
- get pending() {
1320
- return !result();
1321
- },
1334
+ result: response.data,
1335
+ error: response.error,
1322
1336
  clear() {
1323
- router.submissions[1](v => v.filter(i => i !== submission));
1337
+ router.submissions[1](entries => entries.filter(entry => entry !== submission));
1324
1338
  },
1325
1339
  retry() {
1326
- setResult(undefined);
1327
- const p = fn(...variables);
1328
- return p.then(handler(), handler(true));
1340
+ submission.clear();
1341
+ return current[invokeSymbol].call({
1342
+ r: router,
1343
+ f: form
1344
+ }, variables, current);
1329
1345
  }
1330
- }]);
1331
- return p.then(handler(), handler(true));
1346
+ };
1347
+ router.submissions[1](entries => [...entries, submission]);
1348
+ for (const hook of settledHooks.values()) hook(submission);
1349
+ if (response.error && !form) throw response.error;
1350
+ return response.data;
1332
1351
  }
1333
1352
  const o = typeof options === "string" ? {
1334
1353
  name: options
1335
1354
  } : options;
1336
1355
  const name = o.name || (!isServer ? String(hashString(fn.toString())) : undefined);
1337
1356
  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) {
1357
+ const wrapped = toAction(invoke, url);
1358
+ if (name) setFunctionName(wrapped, name);
1359
+ return wrapped;
1360
+ }
1361
+ const action = actionImpl;
1362
+ function toAction(invoke, url, boundArgs = [], base = url, submitHooks = new Map(), settledHooks = new Map()) {
1363
+ const fn = function (...args) {
1364
+ return invoke.call(this, [...boundArgs, ...args], fn);
1365
+ };
1343
1366
  fn.toString = () => {
1344
1367
  if (!url) throw new Error("Client Actions need explicit names if server rendered");
1345
1368
  return url;
1346
1369
  };
1347
1370
  fn.with = function (...args) {
1348
- const newFn = function (...passedArgs) {
1349
- return fn.call(this, ...args, ...passedArgs);
1350
- };
1351
- newFn.base = fn.base;
1352
1371
  const uri = new URL(url, mockBase);
1353
1372
  uri.searchParams.set("args", hashKey(args));
1354
- return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
1373
+ const next = toAction(invoke, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search, [...boundArgs, ...args], base, submitHooks, settledHooks);
1374
+ return next;
1375
+ };
1376
+ fn.onSubmit = function (hook) {
1377
+ const id = Symbol("actionOnSubmitHook");
1378
+ submitHooks.set(id, hook);
1379
+ getOwner() && onCleanup(() => submitHooks.delete(id));
1380
+ return this;
1381
+ };
1382
+ fn.onSettled = function (hook) {
1383
+ const id = Symbol("actionOnSettledHook");
1384
+ settledHooks.set(id, hook);
1385
+ getOwner() && onCleanup(() => settledHooks.delete(id));
1386
+ return this;
1355
1387
  };
1356
1388
  fn.url = url;
1389
+ fn.base = base;
1390
+ fn[submitHooksSymbol] = submitHooks;
1391
+ fn[settledHooksSymbol] = settledHooks;
1392
+ fn[invokeSymbol] = invoke;
1357
1393
  if (!isServer) {
1358
1394
  actions.set(url, fn);
1359
1395
  getOwner() && onCleanup(() => actions.delete(url));
@@ -1361,6 +1397,21 @@ function toAction(fn, url) {
1361
1397
  return fn;
1362
1398
  }
1363
1399
  const hashString = s => s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
1400
+ async function settleActionResult(result) {
1401
+ const value = result;
1402
+ if (value && typeof value.then === "function") {
1403
+ return result.then(value => value);
1404
+ }
1405
+ if (value && typeof value.next === "function") {
1406
+ const iterator = value;
1407
+ let next = await iterator.next();
1408
+ while (!next.done) {
1409
+ next = await iterator.next();
1410
+ }
1411
+ return next.value;
1412
+ }
1413
+ return result;
1414
+ }
1364
1415
  async function handleResponse(response, error, navigate) {
1365
1416
  let data;
1366
1417
  let custom;
@@ -1669,12 +1720,21 @@ function MemoryRouter(props) {
1669
1720
  }
1670
1721
 
1671
1722
  var _tmpl$ = /*#__PURE__*/template(`<a>`);
1723
+ function toClassName(value) {
1724
+ if (!value) return "";
1725
+ if (typeof value === "string" || typeof value === "number") return String(value);
1726
+ if (Array.isArray(value)) return value.map(toClassName).filter(Boolean).join(" ");
1727
+ if (typeof value === "object") {
1728
+ return Object.entries(value).filter(([, enabled]) => enabled).map(([name]) => name).join(" ");
1729
+ }
1730
+ return "";
1731
+ }
1672
1732
  function A(props) {
1673
- props = mergeProps({
1733
+ props = merge({
1674
1734
  inactiveClass: "inactive",
1675
1735
  activeClass: "active"
1676
1736
  }, props);
1677
- const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
1737
+ const rest = omit(props, "href", "state", "class", "activeClass", "inactiveClass", "end");
1678
1738
  const to = useResolvedPath(() => props.href);
1679
1739
  const href = useHref(to);
1680
1740
  const location = useLocation();
@@ -1685,26 +1745,20 @@ function A(props) {
1685
1745
  const loc = decodeURI(normalizePath(location.pathname).toLowerCase());
1686
1746
  return [props.end ? path === loc : loc.startsWith(path + "/") || loc === path, path === loc];
1687
1747
  });
1748
+ const className = createMemo(() => [toClassName(props.class), isActive()[0] ? props.activeClass : props.inactiveClass].filter(Boolean).join(" "));
1688
1749
  return (() => {
1689
1750
  var _el$ = _tmpl$();
1690
- spread(_el$, mergeProps$1(rest, {
1751
+ spread(_el$, mergeProps(rest, {
1691
1752
  get href() {
1692
1753
  return href() || props.href;
1693
1754
  },
1694
1755
  get state() {
1695
1756
  return JSON.stringify(props.state);
1696
1757
  },
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
- };
1758
+ get ["class"]() {
1759
+ return className();
1706
1760
  },
1707
- "link": "",
1761
+ "link": true,
1708
1762
  get ["aria-current"]() {
1709
1763
  return isActive()[1] ? "page" : undefined;
1710
1764
  }
@@ -1730,99 +1784,6 @@ function Navigate(props) {
1730
1784
  return null;
1731
1785
  }
1732
1786
 
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
1787
  function redirect(url, init = 302) {
1827
1788
  let responseInit;
1828
1789
  let revalidate;
@@ -1876,4 +1837,4 @@ function json(data, init = {}) {
1876
1837
  return response;
1877
1838
  }
1878
1839
 
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 };
1840
+ 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 };