@solidjs/router 0.15.3 → 0.16.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 (66) hide show
  1. package/README.md +178 -141
  2. package/dist/data/action.d.ts +1 -1
  3. package/dist/data/createAsync.js +8 -6
  4. package/dist/data/query.js +11 -5
  5. package/dist/data/response.d.ts +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +57 -22
  8. package/dist/routers/HashRouter.d.ts +1 -1
  9. package/dist/routers/Router.js +1 -1
  10. package/dist/routers/components.d.ts +1 -1
  11. package/dist/routers/components.jsx +5 -2
  12. package/dist/routers/createRouter.d.ts +1 -1
  13. package/dist/src/components.d.ts +31 -0
  14. package/dist/src/components.jsx +39 -0
  15. package/dist/src/data/action.d.ts +17 -0
  16. package/dist/src/data/action.js +163 -0
  17. package/dist/src/data/action.spec.d.ts +1 -0
  18. package/dist/src/data/action.spec.js +297 -0
  19. package/dist/src/data/createAsync.d.ts +32 -0
  20. package/dist/src/data/createAsync.js +96 -0
  21. package/dist/src/data/createAsync.spec.d.ts +1 -0
  22. package/dist/src/data/createAsync.spec.js +196 -0
  23. package/dist/src/data/events.d.ts +9 -0
  24. package/dist/src/data/events.js +123 -0
  25. package/dist/src/data/events.spec.d.ts +1 -0
  26. package/dist/src/data/events.spec.js +567 -0
  27. package/dist/src/data/index.d.ts +4 -0
  28. package/dist/src/data/index.js +4 -0
  29. package/dist/src/data/query.d.ts +23 -0
  30. package/dist/src/data/query.js +232 -0
  31. package/dist/src/data/query.spec.d.ts +1 -0
  32. package/dist/src/data/query.spec.js +354 -0
  33. package/dist/src/data/response.d.ts +4 -0
  34. package/dist/src/data/response.js +42 -0
  35. package/dist/src/data/response.spec.d.ts +1 -0
  36. package/dist/src/data/response.spec.js +165 -0
  37. package/dist/src/index.d.ts +7 -0
  38. package/dist/src/index.jsx +6 -0
  39. package/dist/src/lifecycle.d.ts +5 -0
  40. package/dist/src/lifecycle.js +69 -0
  41. package/dist/src/routers/HashRouter.d.ts +9 -0
  42. package/dist/src/routers/HashRouter.js +41 -0
  43. package/dist/src/routers/MemoryRouter.d.ts +24 -0
  44. package/dist/src/routers/MemoryRouter.js +57 -0
  45. package/dist/src/routers/Router.d.ts +9 -0
  46. package/dist/src/routers/Router.js +45 -0
  47. package/dist/src/routers/StaticRouter.d.ts +6 -0
  48. package/dist/src/routers/StaticRouter.js +15 -0
  49. package/dist/src/routers/components.d.ts +27 -0
  50. package/dist/src/routers/components.jsx +118 -0
  51. package/dist/src/routers/createRouter.d.ts +10 -0
  52. package/dist/src/routers/createRouter.js +41 -0
  53. package/dist/src/routers/index.d.ts +11 -0
  54. package/dist/src/routers/index.js +6 -0
  55. package/dist/src/routing.d.ts +175 -0
  56. package/dist/src/routing.js +560 -0
  57. package/dist/src/types.d.ts +200 -0
  58. package/dist/src/types.js +1 -0
  59. package/dist/src/utils.d.ts +13 -0
  60. package/dist/src/utils.js +185 -0
  61. package/dist/test/helpers.d.ts +6 -0
  62. package/dist/test/helpers.js +50 -0
  63. package/dist/types.d.ts +2 -2
  64. package/dist/utils.d.ts +1 -1
  65. package/dist/utils.js +3 -0
  66. package/package.json +3 -2
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
3
3
  */
4
- import { createResource, sharedConfig, untrack } from "solid-js";
4
+ import { createResource, sharedConfig, untrack, catchError } from "solid-js";
5
5
  import { createStore, reconcile, unwrap } from "solid-js/store";
6
6
  import { isServer } from "solid-js/web";
7
7
  export function createAsync(fn, options) {
8
8
  let resource;
9
9
  let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
10
- [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, options);
10
+ [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, options);
11
11
  const resultAccessor = (() => resource());
12
- Object.defineProperty(resultAccessor, 'latest', {
12
+ Object.defineProperty(resultAccessor, "latest", {
13
13
  get() {
14
14
  return resource.latest;
15
15
  }
@@ -18,13 +18,15 @@ export function createAsync(fn, options) {
18
18
  }
19
19
  export function createAsyncStore(fn, options = {}) {
20
20
  let resource;
21
- let prev = () => !resource || resource.state === "unresolved" ? undefined : unwrap(resource.latest);
22
- [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, {
21
+ let prev = () => !resource || resource.state === "unresolved"
22
+ ? undefined
23
+ : unwrap(resource.latest);
24
+ [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, {
23
25
  ...options,
24
26
  storage: (init) => createDeepSignal(init, options.reconcile)
25
27
  });
26
28
  const resultAccessor = (() => resource());
27
- Object.defineProperty(resultAccessor, 'latest', {
29
+ Object.defineProperty(resultAccessor, "latest", {
28
30
  get() {
29
31
  return resource.latest;
30
32
  }
@@ -146,6 +146,15 @@ export function query(fn, name) {
146
146
  function handleResponse(error) {
147
147
  return async (v) => {
148
148
  if (v instanceof Response) {
149
+ const e = getRequestEvent();
150
+ if (e) {
151
+ for (const [key, value] of v.headers) {
152
+ if (key == "set-cookie")
153
+ e.response.headers.append("set-cookie", value);
154
+ else
155
+ e.response.headers.set(key, value);
156
+ }
157
+ }
149
158
  const url = v.headers.get(LocationHeader);
150
159
  if (url !== null) {
151
160
  // client + server relative redirect
@@ -155,11 +164,8 @@ export function query(fn, name) {
155
164
  });
156
165
  else if (!isServer)
157
166
  window.location.href = url;
158
- else if (isServer) {
159
- const e = getRequestEvent();
160
- if (e)
161
- e.response = { status: 302, headers: new Headers({ Location: url }) };
162
- }
167
+ else if (e)
168
+ e.response.status = 302;
163
169
  return;
164
170
  }
165
171
  if (v.customBody)
@@ -1,4 +1,4 @@
1
- import type { RouterResponseInit, CustomResponse } from "../types";
1
+ import type { RouterResponseInit, CustomResponse } from "../types.js";
2
2
  export declare function redirect(url: string, init?: number | RouterResponseInit): CustomResponse<never>;
3
3
  export declare function reload(init?: RouterResponseInit): CustomResponse<never>;
4
4
  export declare function json<T>(data: T, init?: RouterResponseInit): CustomResponse<T>;
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export * from "./lifecycle.js";
4
4
  export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, usePreloadRoute } from "./routing.js";
5
5
  export { mergeSearchString as _mergeSearchString } from "./utils.js";
6
6
  export * from "./data/index.js";
7
- export type { Location, LocationChange, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RoutePreloadFunc, RoutePreloadFuncArgs, RouteDefinition, RouteDescription, RouteMatch, RouterIntegration, RouterUtils, SetParams, Submission, BeforeLeaveEventArgs, RouteLoadFunc, RouteLoadFuncArgs, RouterResponseInit, CustomResponse } from "./types.js";
7
+ export type { Location, LocationChange, SearchParams, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RoutePreloadFunc, RoutePreloadFuncArgs, RouteDefinition, RouteDescription, RouteMatch, RouterIntegration, RouterUtils, SetParams, Submission, BeforeLeaveEventArgs, RouteLoadFunc, RouteLoadFuncArgs, RouterResponseInit, CustomResponse } from "./types.js";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
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 } from 'solid-js';
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
3
  import { createStore, reconcile, unwrap } from 'solid-js/store';
4
4
 
5
5
  function createBeforeLeave() {
@@ -193,6 +193,9 @@ function createMemoObject(fn) {
193
193
  },
194
194
  ownKeys() {
195
195
  return Reflect.ownKeys(fn());
196
+ },
197
+ has(_, property) {
198
+ return property in fn();
196
199
  }
197
200
  });
198
201
  }
@@ -234,8 +237,18 @@ function expandOptionals(pattern) {
234
237
  }
235
238
  return expandOptionals(suffix).reduce((results, expansion) => [...results, ...prefixes.map(p => p + expansion)], []);
236
239
  }
240
+ function setFunctionName(obj, value) {
241
+ Object.defineProperty(obj, "name", {
242
+ value,
243
+ writable: false,
244
+ configurable: false
245
+ });
246
+ return obj;
247
+ }
237
248
 
238
249
  const MAX_REDIRECTS = 100;
250
+
251
+ /** Consider this API opaque and internal. It is likely to change in the future. */
239
252
  const RouterContextObj = createContext();
240
253
  const RouteContextObj = createContext();
241
254
  const useRouter = () => invariant(useContext(RouterContextObj), "<A> and 'use' router primitives can be only used inside a Route.");
@@ -927,7 +940,10 @@ function Routes(props) {
927
940
  }
928
941
  createRoot(dispose => {
929
942
  disposers[i] = dispose;
930
- next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => props.routerState.matches()[i]);
943
+ next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
944
+ const routeMatches = props.routerState.matches();
945
+ return routeMatches[i] ?? routeMatches[0];
946
+ });
931
947
  });
932
948
  }
933
949
  }
@@ -1159,6 +1175,12 @@ function query(fn, name) {
1159
1175
  function handleResponse(error) {
1160
1176
  return async v => {
1161
1177
  if (v instanceof Response) {
1178
+ const e = getRequestEvent();
1179
+ if (e) {
1180
+ for (const [key, value] of v.headers) {
1181
+ if (key == "set-cookie") e.response.headers.append("set-cookie", value);else e.response.headers.set(key, value);
1182
+ }
1183
+ }
1162
1184
  const url = v.headers.get(LocationHeader);
1163
1185
  if (url !== null) {
1164
1186
  // client + server relative redirect
@@ -1166,15 +1188,7 @@ function query(fn, name) {
1166
1188
  navigate(url, {
1167
1189
  replace: true
1168
1190
  });
1169
- });else if (!isServer) window.location.href = url;else if (isServer) {
1170
- const e = getRequestEvent();
1171
- if (e) e.response = {
1172
- status: 302,
1173
- headers: new Headers({
1174
- Location: url
1175
- })
1176
- };
1177
- }
1191
+ });else if (!isServer) window.location.href = url;else if (e) e.response.status = 302;
1178
1192
  return;
1179
1193
  }
1180
1194
  if (v.customBody) v = await v.customBody();
@@ -1319,8 +1333,10 @@ function action(fn, options = {}) {
1319
1333
  const o = typeof options === "string" ? {
1320
1334
  name: options
1321
1335
  } : options;
1322
- const url = fn.url || o.name && `https://action/${o.name}` || (!isServer ? `https://action/${hashString(fn.toString())}` : "");
1336
+ const name = o.name || (!isServer ? String(hashString(fn.toString())) : undefined);
1337
+ const url = fn.url || name && `https://action/${name}` || "";
1323
1338
  mutate.base = url;
1339
+ if (name) setFunctionName(mutate, name);
1324
1340
  return toAction(mutate, url);
1325
1341
  }
1326
1342
  function toAction(fn, url) {
@@ -1382,7 +1398,12 @@ async function handleResponse(response, error, navigate) {
1382
1398
  } : undefined;
1383
1399
  }
1384
1400
 
1385
- function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server", transformUrl) {
1401
+ function setupNativeEvents({
1402
+ preload = true,
1403
+ explicitLinks = false,
1404
+ actionBase = "/_server",
1405
+ transformUrl
1406
+ } = {}) {
1386
1407
  return router => {
1387
1408
  const basePath = router.base.path();
1388
1409
  const navigateFromRoute = router.navigatorFactory(router.base);
@@ -1515,7 +1536,7 @@ function Router(props) {
1515
1536
  saveCurrentDepth();
1516
1537
  },
1517
1538
  init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
1518
- if (delta && delta < 0) {
1539
+ if (delta) {
1519
1540
  return !beforeLeave.confirm(delta);
1520
1541
  } else {
1521
1542
  const s = getSource();
@@ -1524,7 +1545,12 @@ function Router(props) {
1524
1545
  });
1525
1546
  }
1526
1547
  })),
1527
- create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase, props.transformUrl),
1548
+ create: setupNativeEvents({
1549
+ preload: props.preload,
1550
+ explicitLinks: props.explicitLinks,
1551
+ actionBase: props.actionBase,
1552
+ transformUrl: props.transformUrl
1553
+ }),
1528
1554
  utils: {
1529
1555
  go: delta => window.history.go(delta),
1530
1556
  beforeLeave
@@ -1565,7 +1591,11 @@ function HashRouter(props) {
1565
1591
  saveCurrentDepth();
1566
1592
  },
1567
1593
  init: notify => bindEvent(window, "hashchange", notifyIfNotBlocked(notify, delta => !beforeLeave.confirm(delta && delta < 0 ? delta : getSource()))),
1568
- create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
1594
+ create: setupNativeEvents({
1595
+ preload: props.preload,
1596
+ explicitLinks: props.explicitLinks,
1597
+ actionBase: props.actionBase
1598
+ }),
1569
1599
  utils: {
1570
1600
  go: delta => window.history.go(delta),
1571
1601
  renderPath: path => `#${path}`,
@@ -1627,7 +1657,11 @@ function MemoryRouter(props) {
1627
1657
  get: memoryHistory.get,
1628
1658
  set: memoryHistory.set,
1629
1659
  init: memoryHistory.listen,
1630
- create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
1660
+ create: setupNativeEvents({
1661
+ preload: props.preload,
1662
+ explicitLinks: props.explicitLinks,
1663
+ actionBase: props.actionBase
1664
+ }),
1631
1665
  utils: {
1632
1666
  go: memoryHistory.go
1633
1667
  }
@@ -1709,9 +1743,10 @@ function Navigate(props) {
1709
1743
  function createAsync(fn, options) {
1710
1744
  let resource;
1711
1745
  let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
1712
- [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, options);
1746
+ [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, options);
1713
1747
  const resultAccessor = () => resource();
1714
- Object.defineProperty(resultAccessor, 'latest', {
1748
+ if (options?.name) setFunctionName(resultAccessor, options.name);
1749
+ Object.defineProperty(resultAccessor, "latest", {
1715
1750
  get() {
1716
1751
  return resource.latest;
1717
1752
  }
@@ -1721,12 +1756,12 @@ function createAsync(fn, options) {
1721
1756
  function createAsyncStore(fn, options = {}) {
1722
1757
  let resource;
1723
1758
  let prev = () => !resource || resource.state === "unresolved" ? undefined : unwrap(resource.latest);
1724
- [resource] = createResource(() => subFetch(fn, untrack(prev)), v => v, {
1759
+ [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, {
1725
1760
  ...options,
1726
1761
  storage: init => createDeepSignal(init, options.reconcile)
1727
1762
  });
1728
1763
  const resultAccessor = () => resource();
1729
- Object.defineProperty(resultAccessor, 'latest', {
1764
+ Object.defineProperty(resultAccessor, "latest", {
1730
1765
  get() {
1731
1766
  return resource.latest;
1732
1767
  }
@@ -1841,4 +1876,4 @@ function json(data, init = {}) {
1841
1876
  return response;
1842
1877
  }
1843
1878
 
1844
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, 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 };
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 };
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from "solid-js";
2
- import type { BaseRouterProps } from "./components.js";
2
+ import type { BaseRouterProps } from "./components.jsx";
3
3
  export declare function hashParser(str: string): string;
4
4
  export type HashRouterProps = BaseRouterProps & {
5
5
  actionBase?: string;
@@ -28,7 +28,7 @@ export function Router(props) {
28
28
  saveCurrentDepth();
29
29
  },
30
30
  init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
31
- if (delta && delta < 0) {
31
+ if (delta) {
32
32
  return !beforeLeave.confirm(delta);
33
33
  }
34
34
  else {
@@ -1,5 +1,5 @@
1
1
  import type { Component, JSX } from "solid-js";
2
- import type { MatchFilters, RouteDefinition, RouterIntegration, RouteSectionProps, RoutePreloadFunc } from "../types.js";
2
+ import type { MatchFilters, RouteDefinition, RoutePreloadFunc, RouterIntegration, RouteSectionProps } from "../types.js";
3
3
  export type BaseRouterProps = {
4
4
  base?: string;
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  /*@refresh skip*/
2
- import { getRequestEvent, isServer } from "solid-js/web";
3
2
  import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
3
+ import { getRequestEvent, isServer } from "solid-js/web";
4
4
  import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInPreloadFn } from "../routing.js";
5
5
  export const createRouterComponent = (router) => (props) => {
6
6
  const { base } = props;
@@ -70,7 +70,10 @@ function Routes(props) {
70
70
  }
71
71
  createRoot(dispose => {
72
72
  disposers[i] = dispose;
73
- next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => props.routerState.matches()[i]);
73
+ next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
74
+ const routeMatches = props.routerState.matches();
75
+ return routeMatches[i] ?? routeMatches[0];
76
+ });
74
77
  });
75
78
  }
76
79
  }
@@ -1,4 +1,4 @@
1
- import type { LocationChange, RouterContext, RouterUtils } from "../types.ts";
1
+ import type { LocationChange, RouterContext, RouterUtils } from "../types.js";
2
2
  export declare function createRouter(config: {
3
3
  get: () => string | LocationChange;
4
4
  set: (next: LocationChange) => void;
@@ -0,0 +1,31 @@
1
+ import type { JSX } from "solid-js";
2
+ import type { Location, Navigator } from "./types.js";
3
+ declare module "solid-js" {
4
+ namespace JSX {
5
+ interface AnchorHTMLAttributes<T> {
6
+ state?: string;
7
+ noScroll?: boolean;
8
+ replace?: boolean;
9
+ preload?: boolean;
10
+ link?: boolean;
11
+ }
12
+ }
13
+ }
14
+ export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, "state"> {
15
+ href: string;
16
+ replace?: boolean | undefined;
17
+ noScroll?: boolean | undefined;
18
+ state?: unknown | undefined;
19
+ inactiveClass?: string | undefined;
20
+ activeClass?: string | undefined;
21
+ end?: boolean | undefined;
22
+ }
23
+ export declare function A(props: AnchorProps): JSX.Element;
24
+ export interface NavigateProps {
25
+ href: ((args: {
26
+ navigate: Navigator;
27
+ location: Location;
28
+ }) => string) | string;
29
+ state?: unknown;
30
+ }
31
+ export declare function Navigate(props: NavigateProps): null;
@@ -0,0 +1,39 @@
1
+ import { createMemo, mergeProps, splitProps } from "solid-js";
2
+ import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing.js";
3
+ import { normalizePath } from "./utils.js";
4
+ export function A(props) {
5
+ props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
6
+ const [, rest] = splitProps(props, [
7
+ "href",
8
+ "state",
9
+ "class",
10
+ "activeClass",
11
+ "inactiveClass",
12
+ "end"
13
+ ]);
14
+ const to = useResolvedPath(() => props.href);
15
+ const href = useHref(to);
16
+ const location = useLocation();
17
+ const isActive = createMemo(() => {
18
+ const to_ = to();
19
+ if (to_ === undefined)
20
+ return [false, false];
21
+ const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
22
+ const loc = decodeURI(normalizePath(location.pathname).toLowerCase());
23
+ return [props.end ? path === loc : loc.startsWith(path + "/") || loc === path, path === loc];
24
+ });
25
+ return (<a {...rest} href={href() || props.href} state={JSON.stringify(props.state)} classList={{
26
+ ...(props.class && { [props.class]: true }),
27
+ [props.inactiveClass]: !isActive()[0],
28
+ [props.activeClass]: isActive()[0],
29
+ ...rest.classList
30
+ }} link aria-current={isActive()[1] ? "page" : undefined}/>);
31
+ }
32
+ export function Navigate(props) {
33
+ const navigate = useNavigate();
34
+ const location = useLocation();
35
+ const { href, state } = props;
36
+ const path = typeof href === "function" ? href({ navigate, location }) : href;
37
+ navigate(path, { replace: true, state });
38
+ return null;
39
+ }
@@ -0,0 +1,17 @@
1
+ import { JSX } from "solid-js";
2
+ import type { Submission, SubmissionStub, NarrowResponse } from "../types.js";
3
+ export type Action<T extends Array<any>, U, V = T> = (T extends [FormData | URLSearchParams] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<NarrowResponse<U>>) & {
4
+ url: string;
5
+ with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<NarrowResponse<U>>, ...args: A): Action<B, U, V>;
6
+ };
7
+ export declare const actions: Map<string, Action<any, any, any>>;
8
+ export declare function useSubmissions<T extends Array<any>, U, V>(fn: Action<T, U, V>, filter?: (input: V) => boolean): Submission<T, NarrowResponse<U>>[] & {
9
+ pending: boolean;
10
+ };
11
+ export declare function useSubmission<T extends Array<any>, U, V>(fn: Action<T, U, V>, filter?: (input: V) => boolean): Submission<T, NarrowResponse<U>> | SubmissionStub;
12
+ export declare function useAction<T extends Array<any>, U, V>(action: Action<T, U, V>): (...args: Parameters<Action<T, U, V>>) => Promise<NarrowResponse<U>>;
13
+ export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>;
14
+ export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, options?: {
15
+ name?: string;
16
+ onComplete?: (s: Submission<T, U>) => void;
17
+ }): Action<T, U>;
@@ -0,0 +1,163 @@
1
+ import { $TRACK, createMemo, createSignal, onCleanup, getOwner } from "solid-js";
2
+ import { isServer } from "solid-js/web";
3
+ import { useRouter } from "../routing.js";
4
+ import { mockBase, setFunctionName } from "../utils.js";
5
+ import { cacheKeyOp, hashKey, revalidate, query } from "./query.js";
6
+ export const actions = /* #__PURE__ */ new Map();
7
+ export function useSubmissions(fn, filter) {
8
+ const router = useRouter();
9
+ const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.base && (!filter || filter(s.input))));
10
+ return new Proxy([], {
11
+ get(_, property) {
12
+ if (property === $TRACK)
13
+ return subs();
14
+ if (property === "pending")
15
+ return subs().some(sub => !sub.result);
16
+ return subs()[property];
17
+ },
18
+ has(_, property) {
19
+ return property in subs();
20
+ }
21
+ });
22
+ }
23
+ export function useSubmission(fn, filter) {
24
+ const submissions = useSubmissions(fn, filter);
25
+ return new Proxy({}, {
26
+ get(_, property) {
27
+ if ((submissions.length === 0 && property === "clear") || property === "retry")
28
+ return () => { };
29
+ return submissions[submissions.length - 1]?.[property];
30
+ }
31
+ });
32
+ }
33
+ export function useAction(action) {
34
+ const r = useRouter();
35
+ return (...args) => action.apply({ r }, args);
36
+ }
37
+ export function action(fn, options = {}) {
38
+ function mutate(...variables) {
39
+ const router = this.r;
40
+ const form = this.f;
41
+ const p = (router.singleFlight && fn.withOptions
42
+ ? fn.withOptions({ headers: { "X-Single-Flight": "true" } })
43
+ : fn)(...variables);
44
+ const [result, setResult] = createSignal();
45
+ let submission;
46
+ function handler(error) {
47
+ return async (res) => {
48
+ const result = await handleResponse(res, error, router.navigatorFactory());
49
+ let retry = null;
50
+ o.onComplete?.({
51
+ ...submission,
52
+ result: result?.data,
53
+ error: result?.error,
54
+ pending: false,
55
+ retry() {
56
+ return (retry = submission.retry());
57
+ }
58
+ });
59
+ if (retry)
60
+ return retry;
61
+ if (!result)
62
+ return submission.clear();
63
+ setResult(result);
64
+ if (result.error && !form)
65
+ throw result.error;
66
+ return result.data;
67
+ };
68
+ }
69
+ router.submissions[1](s => [
70
+ ...s,
71
+ (submission = {
72
+ input: variables,
73
+ url,
74
+ get result() {
75
+ return result()?.data;
76
+ },
77
+ get error() {
78
+ return result()?.error;
79
+ },
80
+ get pending() {
81
+ return !result();
82
+ },
83
+ clear() {
84
+ router.submissions[1](v => v.filter(i => i !== submission));
85
+ },
86
+ retry() {
87
+ setResult(undefined);
88
+ const p = fn(...variables);
89
+ return p.then(handler(), handler(true));
90
+ }
91
+ })
92
+ ]);
93
+ return p.then(handler(), handler(true));
94
+ }
95
+ const o = typeof options === "string" ? { name: options } : options;
96
+ const name = o.name || (!isServer ? String(hashString(fn.toString())) : undefined);
97
+ const url = fn.url || (name && `https://action/${name}`) || "";
98
+ mutate.base = url;
99
+ if (name)
100
+ setFunctionName(mutate, name);
101
+ return toAction(mutate, url);
102
+ }
103
+ function toAction(fn, url) {
104
+ fn.toString = () => {
105
+ if (!url)
106
+ throw new Error("Client Actions need explicit names if server rendered");
107
+ return url;
108
+ };
109
+ fn.with = function (...args) {
110
+ const newFn = function (...passedArgs) {
111
+ return fn.call(this, ...args, ...passedArgs);
112
+ };
113
+ newFn.base = fn.base;
114
+ const uri = new URL(url, mockBase);
115
+ uri.searchParams.set("args", hashKey(args));
116
+ return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
117
+ };
118
+ fn.url = url;
119
+ if (!isServer) {
120
+ actions.set(url, fn);
121
+ getOwner() && onCleanup(() => actions.delete(url));
122
+ }
123
+ return fn;
124
+ }
125
+ const hashString = (s) => s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
126
+ async function handleResponse(response, error, navigate) {
127
+ let data;
128
+ let custom;
129
+ let keys;
130
+ let flightKeys;
131
+ if (response instanceof Response) {
132
+ if (response.headers.has("X-Revalidate"))
133
+ keys = response.headers.get("X-Revalidate").split(",");
134
+ if (response.customBody) {
135
+ data = custom = await response.customBody();
136
+ if (response.headers.has("X-Single-Flight")) {
137
+ data = data._$value;
138
+ delete custom._$value;
139
+ flightKeys = Object.keys(custom);
140
+ }
141
+ }
142
+ if (response.headers.has("Location")) {
143
+ const locationUrl = response.headers.get("Location") || "/";
144
+ if (locationUrl.startsWith("http")) {
145
+ window.location.href = locationUrl;
146
+ }
147
+ else {
148
+ navigate(locationUrl);
149
+ }
150
+ }
151
+ }
152
+ else if (error)
153
+ return { error: response };
154
+ else
155
+ data = response;
156
+ // invalidate
157
+ cacheKeyOp(keys, entry => (entry[0] = 0));
158
+ // set cache
159
+ flightKeys && flightKeys.forEach(k => query.set(k, custom[k]));
160
+ // trigger revalidation
161
+ await revalidate(keys, false);
162
+ return data != null ? { data } : undefined;
163
+ }
@@ -0,0 +1 @@
1
+ export {};