@solidjs/router 0.10.0-beta.5 → 0.10.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -382,12 +382,12 @@ const user = createAsync(() => getUser(params.id))
382
382
 
383
383
  Actions are data mutations that can trigger invalidations and further routing. A list of prebuilt response builders can be found below(TODO).
384
384
  ```jsx
385
+ import { action, revalidate, redirect } from "@solidjs/router"
386
+
385
387
  // anywhere
386
388
  const myAction = action(async (data) => {
387
389
  await doMutation(data);
388
- return redirect("/", {
389
- invalidate: [getUser, data.id]
390
- }) // returns a response
390
+ throw redirect("/", { revalidate: getUser.keyFor(data.id) }); // throw a response to do a redirect
391
391
  });
392
392
 
393
393
  // in component
@@ -1,5 +1,3 @@
1
- /*@refresh skip*/
2
- "use client";
3
1
  import { createMemo, mergeProps, splitProps } from "solid-js";
4
2
  import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing";
5
3
  import { normalizePath } from "./utils";
@@ -1,10 +1,13 @@
1
1
  import { JSX } from "solid-js";
2
2
  import { Submission } from "../types";
3
- export type Action<T, U> = ((vars: T) => Promise<U>) & JSX.SerializableAttributeValue;
4
- export declare const actions: Map<string, Function>;
5
- export declare function useSubmissions<T, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U>[] & {
3
+ export type Action<T extends Array<any>, U> = ((...vars: T) => Promise<U>) & JSX.SerializableAttributeValue & {
4
+ url: string;
5
+ with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => U, ...args: A): Action<B, U>;
6
+ };
7
+ export declare const actions: Map<string, Action<any, any>>;
8
+ export declare function useSubmissions<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U>[] & {
6
9
  pending: boolean;
7
10
  };
8
- export declare function useSubmission<T, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U>;
9
- export declare function useAction<T, U>(action: Action<T, U>): Action<T, U>;
10
- export declare function action<T, U = void>(fn: (args: T) => Promise<U>, name?: string): Action<T, U>;
11
+ export declare function useSubmission<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U>;
12
+ export declare function useAction<T extends Array<any>, U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>;
13
+ export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>;
@@ -1,8 +1,8 @@
1
- import { $TRACK, createMemo, createSignal } from "solid-js";
1
+ import { $TRACK, createMemo, createSignal, onCleanup, getOwner } from "solid-js";
2
2
  import { isServer } from "solid-js/web";
3
3
  import { useRouter } from "../routing";
4
4
  import { redirectStatusCodes } from "../utils";
5
- import { revalidate } from "./cache";
5
+ import { hashKey, revalidate } from "./cache";
6
6
  export const actions = /* #__PURE__ */ new Map();
7
7
  export function useSubmissions(fn, filter) {
8
8
  const router = useRouter();
@@ -27,11 +27,11 @@ export function useSubmission(fn, filter) {
27
27
  }
28
28
  export function useAction(action) {
29
29
  const router = useRouter();
30
- return action.bind(router);
30
+ return (...args) => action.apply(router, args);
31
31
  }
32
32
  export function action(fn, name) {
33
- function mutate(variables) {
34
- const p = fn(variables);
33
+ function mutate(...variables) {
34
+ const p = fn(...variables);
35
35
  const [result, setResult] = createSignal();
36
36
  let submission;
37
37
  const router = this;
@@ -56,7 +56,7 @@ export function action(fn, name) {
56
56
  },
57
57
  retry() {
58
58
  setResult(undefined);
59
- const p = fn(variables);
59
+ const p = fn(...variables);
60
60
  p.then(handler, handler);
61
61
  return p;
62
62
  }
@@ -66,29 +66,50 @@ export function action(fn, name) {
66
66
  return p;
67
67
  }
68
68
  const url = fn.url || (name && `action:${name}`) || (!isServer ? `action:${fn.name}` : "");
69
- mutate.toString = () => {
69
+ return toAction(mutate, url);
70
+ }
71
+ function toAction(fn, url) {
72
+ fn.toString = () => {
70
73
  if (!url)
71
74
  throw new Error("Client Actions need explicit names if server rendered");
72
75
  return url;
73
76
  };
74
- if (!isServer)
75
- actions.set(url, mutate);
76
- return mutate;
77
+ fn.with = function (...args) {
78
+ const newFn = function (...passedArgs) {
79
+ return fn.call(this, ...args, ...passedArgs);
80
+ };
81
+ const uri = new URL(url, "http://sar");
82
+ uri.searchParams.set("args", hashKey(args));
83
+ return toAction(newFn, uri.toString());
84
+ };
85
+ fn.url = url;
86
+ if (!isServer) {
87
+ actions.set(url, fn);
88
+ getOwner() && onCleanup(() => actions.delete(url));
89
+ }
90
+ return fn;
77
91
  }
78
92
  async function handleResponse(response, navigate) {
79
93
  let data;
80
- if (response instanceof Response && redirectStatusCodes.has(response.status)) {
81
- const locationUrl = response.headers.get("Location") || "/";
82
- if (locationUrl.startsWith("http")) {
83
- window.location.href = locationUrl;
94
+ let keys;
95
+ if (response instanceof Response) {
96
+ if (response.headers.has("X-Revalidate")) {
97
+ keys = response.headers.get("X-Revalidate").split(",");
84
98
  }
85
- else {
86
- navigate(locationUrl);
99
+ if (response.customBody)
100
+ data = await response.customBody();
101
+ if (redirectStatusCodes.has(response.status)) {
102
+ const locationUrl = response.headers.get("Location") || "/";
103
+ if (locationUrl.startsWith("http")) {
104
+ window.location.href = locationUrl;
105
+ }
106
+ else {
107
+ navigate(locationUrl);
108
+ }
87
109
  }
88
110
  }
89
111
  else
90
112
  data = response;
91
- // TODO: handle keys
92
- await revalidate();
113
+ await revalidate(keys);
93
114
  return data;
94
115
  }
@@ -1,3 +1,8 @@
1
1
  import { type ReconcileOptions } from "solid-js/store";
2
- export declare function revalidate(key?: string | any[] | void): Promise<void>;
3
- export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): T;
2
+ export declare function revalidate(key?: string | string[] | void): Promise<void>;
3
+ export type CachedFunction<T extends (...args: any) => U | Response, U> = T & {
4
+ keyFor: (...args: Parameters<T>) => string;
5
+ key: string;
6
+ };
7
+ export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): CachedFunction<T, U>;
8
+ export declare function hashKey<T extends Array<any>>(args: T): string;
@@ -5,7 +5,19 @@ import { useNavigate, getIntent } from "../routing";
5
5
  import { redirectStatusCodes } from "../utils";
6
6
  const LocationHeader = "Location";
7
7
  const PRELOAD_TIMEOUT = 5000;
8
+ const CACHE_TIMEOUT = 180000;
8
9
  let cacheMap = new Map();
10
+ // cleanup forward/back cache
11
+ if (!isServer) {
12
+ setInterval(() => {
13
+ const now = Date.now();
14
+ for (let [k, v] of cacheMap.entries()) {
15
+ if (!v[3].size && now - v[0] > CACHE_TIMEOUT) {
16
+ cacheMap.delete(k);
17
+ }
18
+ }
19
+ }, 300000);
20
+ }
9
21
  function getCache() {
10
22
  if (!isServer)
11
23
  return cacheMap;
@@ -13,13 +25,14 @@ function getCache() {
13
25
  return req.routerCache || (req.routerCache = new Map());
14
26
  }
15
27
  export function revalidate(key) {
28
+ key && !Array.isArray(key) && (key = [key]);
16
29
  return startTransition(() => {
17
30
  const now = Date.now();
18
31
  for (let k of cacheMap.keys()) {
19
- if (key === undefined || k === key) {
20
- const set = cacheMap.get(k)[3];
21
- revalidateSignals(set, now);
22
- cacheMap.delete(k);
32
+ if (key === undefined || matchKey(k, key)) {
33
+ const entry = cacheMap.get(k);
34
+ entry[0] = 0; //force cache miss
35
+ revalidateSignals(entry[3], now); // retrigger live signals
23
36
  }
24
37
  }
25
38
  });
@@ -30,13 +43,13 @@ function revalidateSignals(set, time) {
30
43
  }
31
44
  export function cache(fn, name, options) {
32
45
  const [store, setStore] = createStore({});
33
- return ((...args) => {
46
+ const cachedFn = ((...args) => {
34
47
  const cache = getCache();
35
48
  const intent = getIntent();
36
49
  const owner = getOwner();
37
50
  const navigate = owner ? useNavigate() : undefined;
38
51
  const now = Date.now();
39
- const key = name + (args.length ? ":" + args.join(":") : "");
52
+ const key = name + hashKey(args);
40
53
  let cached = cache.get(key);
41
54
  let version;
42
55
  if (owner) {
@@ -53,9 +66,10 @@ export function cache(fn, name, options) {
53
66
  }
54
67
  let res = cached[1];
55
68
  if (!isServer && intent === "navigate") {
56
- res = "then" in cached[1]
57
- ? cached[1].then(handleResponse(false), handleResponse(true))
58
- : handleResponse(false)(cached[1]);
69
+ res =
70
+ "then" in cached[1]
71
+ ? cached[1].then(handleResponse(false), handleResponse(true))
72
+ : handleResponse(false)(cached[1]);
59
73
  startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
60
74
  }
61
75
  return res;
@@ -79,9 +93,10 @@ export function cache(fn, name, options) {
79
93
  else
80
94
  cache.set(key, (cached = [now, res, intent, new Set(version ? [version] : [])]));
81
95
  if (intent !== "preload") {
82
- res = "then" in res
83
- ? res.then(handleResponse(false), handleResponse(true))
84
- : handleResponse(false)(res);
96
+ res =
97
+ "then" in res
98
+ ? res.then(handleResponse(false), handleResponse(true))
99
+ : handleResponse(false)(res);
85
100
  }
86
101
  return res;
87
102
  function handleResponse(error) {
@@ -111,4 +126,32 @@ export function cache(fn, name, options) {
111
126
  };
112
127
  }
113
128
  });
129
+ cachedFn.keyFor = (...args) => name + hashKey(args);
130
+ cachedFn.key = name;
131
+ return cachedFn;
132
+ }
133
+ function matchKey(key, keys) {
134
+ for (let k of keys) {
135
+ if (key.startsWith(k))
136
+ return true;
137
+ }
138
+ return false;
139
+ }
140
+ // Modified from the amazing Tanstack Query library (MIT)
141
+ // https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168
142
+ export function hashKey(args) {
143
+ return JSON.stringify(args, (_, val) => isPlainObject(val)
144
+ ? Object.keys(val)
145
+ .sort()
146
+ .reduce((result, key) => {
147
+ result[key] = val[key];
148
+ return result;
149
+ }, {})
150
+ : val);
151
+ }
152
+ function isPlainObject(obj) {
153
+ let proto;
154
+ return (obj != null &&
155
+ typeof obj === "object" &&
156
+ (!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype));
114
157
  }
@@ -81,7 +81,9 @@ export function setupNativeEvents(router) {
81
81
  }
82
82
  }
83
83
  function handleFormSubmit(evt) {
84
- let actionRef = (evt.submitter && evt.submitter.getAttribute("formaction")) || evt.target.action;
84
+ let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction")
85
+ ? evt.submitter.formAction
86
+ : evt.target.action;
85
87
  if (!actionRef)
86
88
  return;
87
89
  if (!actionRef.startsWith("action:")) {
@@ -1,4 +1,4 @@
1
1
  export { createAsync } from "./createAsync";
2
- export { action, useSubmission, useSubmissions, useAction } from "./action";
3
- export { cache, revalidate } from "./cache";
2
+ export { action, useSubmission, useSubmissions, useAction, type Action } from "./action";
3
+ export { cache, revalidate, type CachedFunction } from "./cache";
4
4
  export { redirect } from "./response";
@@ -1 +1,5 @@
1
- export declare function redirect(url: string, init?: number | ResponseInit): Response;
1
+ export type RouterResponseInit = ResponseInit & {
2
+ revalidate?: string | string[];
3
+ };
4
+ export declare function redirect(url: string, init?: number | RouterResponseInit): Response;
5
+ export declare function reload(init: RouterResponseInit): Response;
@@ -1,16 +1,28 @@
1
1
  export function redirect(url, init = 302) {
2
- let responseInit = init;
3
- if (typeof responseInit === "number") {
4
- responseInit = { status: responseInit };
2
+ let responseInit;
3
+ let revalidate;
4
+ if (typeof init === "number") {
5
+ responseInit = { status: init };
5
6
  }
6
- else if (typeof responseInit.status === "undefined") {
7
- responseInit.status = 302;
7
+ else {
8
+ ({ revalidate, ...responseInit } = init);
9
+ if (typeof responseInit.status === "undefined") {
10
+ responseInit.status = 302;
11
+ }
8
12
  }
9
13
  const headers = new Headers(responseInit.headers);
10
14
  headers.set("Location", url);
15
+ revalidate && headers.set("X-Revalidate", revalidate.toString());
11
16
  const response = new Response(null, {
12
17
  ...responseInit,
13
18
  headers: headers
14
19
  });
15
20
  return response;
16
21
  }
22
+ export function reload(init) {
23
+ const { revalidate, ...responseInit } = init;
24
+ return new Response(null, {
25
+ ...responseInit,
26
+ ...(revalidate ? { headers: new Headers(responseInit.headers).set("X-Revalidate", revalidate.toString()) } : {})
27
+ });
28
+ }
package/dist/index.js CHANGED
@@ -389,7 +389,7 @@ function createRouterContext(integration, getBranches, options = {}) {
389
389
  const [state, setState] = createSignal(source().state);
390
390
  const location = createLocation(reference, state);
391
391
  const referrers = [];
392
- const submissions = createSignal(initFromFlash(location.query));
392
+ const submissions = createSignal(isServer ? initFromFlash() : []);
393
393
  const baseRoute = {
394
394
  pattern: basePath,
395
395
  params: {},
@@ -544,15 +544,9 @@ function createRouterContext(integration, getBranches, options = {}) {
544
544
  }
545
545
  intent = prevIntent;
546
546
  }
547
- function initFromFlash(params) {
548
- let param = params.form ? JSON.parse(params.form) : null;
549
- if (!param || !param.result) return [];
550
- const input = new Map(param.entries);
551
- return [{
552
- url: param.url,
553
- result: param.error ? new Error(param.result) : param.result,
554
- input: input
555
- }];
547
+ function initFromFlash() {
548
+ const e = getRequestEvent();
549
+ return e && e.initialSubmission ? [e.initialSubmission] : [];
556
550
  }
557
551
  }
558
552
  function createRouteContext(router, parent, outlet, match, params) {
@@ -586,7 +580,7 @@ function createRouteContext(router, parent, outlet, match, params) {
586
580
  load && load({
587
581
  params,
588
582
  location,
589
- intent: intent || "navigate"
583
+ intent: intent || "initial"
590
584
  });
591
585
  return route;
592
586
  }
@@ -757,36 +751,51 @@ function StaticRouter(props) {
757
751
 
758
752
  const LocationHeader = "Location";
759
753
  const PRELOAD_TIMEOUT = 5000;
754
+ const CACHE_TIMEOUT = 180000;
760
755
  let cacheMap = new Map();
756
+
757
+ // cleanup forward/back cache
758
+ if (!isServer) {
759
+ setInterval(() => {
760
+ const now = Date.now();
761
+ for (let [k, v] of cacheMap.entries()) {
762
+ if (!v[3].size && now - v[0] > CACHE_TIMEOUT) {
763
+ cacheMap.delete(k);
764
+ }
765
+ }
766
+ }, 300000);
767
+ }
761
768
  function getCache() {
762
769
  if (!isServer) return cacheMap;
763
770
  const req = getRequestEvent() || sharedConfig.context;
764
771
  return req.routerCache || (req.routerCache = new Map());
765
772
  }
766
773
  function revalidate(key) {
774
+ key && !Array.isArray(key) && (key = [key]);
767
775
  return startTransition(() => {
768
776
  const now = Date.now();
769
777
  for (let k of cacheMap.keys()) {
770
- if (key === undefined || k === key) {
771
- const set = cacheMap.get(k)[3];
772
- revalidateSignals(set, now);
773
- cacheMap.delete(k);
778
+ if (key === undefined || matchKey(k, key)) {
779
+ const entry = cacheMap.get(k);
780
+ entry[0] = 0; //force cache miss
781
+ revalidateSignals(entry[3], now); // retrigger live signals
774
782
  }
775
783
  }
776
784
  });
777
785
  }
786
+
778
787
  function revalidateSignals(set, time) {
779
788
  for (let s of set) s[1](time);
780
789
  }
781
790
  function cache(fn, name, options) {
782
791
  const [store, setStore] = createStore({});
783
- return (...args) => {
792
+ const cachedFn = (...args) => {
784
793
  const cache = getCache();
785
794
  const intent = getIntent();
786
795
  const owner = getOwner();
787
796
  const navigate = owner ? useNavigate() : undefined;
788
797
  const now = Date.now();
789
- const key = name + (args.length ? ":" + args.join(":") : "");
798
+ const key = name + hashKey(args);
790
799
  let cached = cache.get(key);
791
800
  let version;
792
801
  if (owner) {
@@ -855,6 +864,28 @@ function cache(fn, name, options) {
855
864
  };
856
865
  }
857
866
  };
867
+ cachedFn.keyFor = (...args) => name + hashKey(args);
868
+ cachedFn.key = name;
869
+ return cachedFn;
870
+ }
871
+ function matchKey(key, keys) {
872
+ for (let k of keys) {
873
+ if (key.startsWith(k)) return true;
874
+ }
875
+ return false;
876
+ }
877
+
878
+ // Modified from the amazing Tanstack Query library (MIT)
879
+ // https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168
880
+ function hashKey(args) {
881
+ return JSON.stringify(args, (_, val) => isPlainObject(val) ? Object.keys(val).sort().reduce((result, key) => {
882
+ result[key] = val[key];
883
+ return result;
884
+ }, {}) : val);
885
+ }
886
+ function isPlainObject(obj) {
887
+ let proto;
888
+ return obj != null && typeof obj === "object" && (!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype);
858
889
  }
859
890
 
860
891
  const actions = /* #__PURE__ */new Map();
@@ -879,11 +910,11 @@ function useSubmission(fn, filter) {
879
910
  }
880
911
  function useAction(action) {
881
912
  const router = useRouter();
882
- return action.bind(router);
913
+ return (...args) => action.apply(router, args);
883
914
  }
884
915
  function action(fn, name) {
885
- function mutate(variables) {
886
- const p = fn(variables);
916
+ function mutate(...variables) {
917
+ const p = fn(...variables);
887
918
  const [result, setResult] = createSignal();
888
919
  let submission;
889
920
  const router = this;
@@ -908,7 +939,7 @@ function action(fn, name) {
908
939
  },
909
940
  retry() {
910
941
  setResult(undefined);
911
- const p = fn(variables);
942
+ const p = fn(...variables);
912
943
  p.then(handler, handler);
913
944
  return p;
914
945
  }
@@ -917,25 +948,46 @@ function action(fn, name) {
917
948
  return p;
918
949
  }
919
950
  const url = fn.url || name && `action:${name}` || (!isServer ? `action:${fn.name}` : "");
920
- mutate.toString = () => {
951
+ return toAction(mutate, url);
952
+ }
953
+ function toAction(fn, url) {
954
+ fn.toString = () => {
921
955
  if (!url) throw new Error("Client Actions need explicit names if server rendered");
922
956
  return url;
923
957
  };
924
- if (!isServer) actions.set(url, mutate);
925
- return mutate;
958
+ fn.with = function (...args) {
959
+ const newFn = function (...passedArgs) {
960
+ return fn.call(this, ...args, ...passedArgs);
961
+ };
962
+ const uri = new URL(url, "http://sar");
963
+ uri.searchParams.set("args", hashKey(args));
964
+ return toAction(newFn, uri.toString());
965
+ };
966
+ fn.url = url;
967
+ if (!isServer) {
968
+ actions.set(url, fn);
969
+ getOwner() && onCleanup(() => actions.delete(url));
970
+ }
971
+ return fn;
926
972
  }
927
973
  async function handleResponse(response, navigate) {
928
974
  let data;
929
- if (response instanceof Response && redirectStatusCodes.has(response.status)) {
930
- const locationUrl = response.headers.get("Location") || "/";
931
- if (locationUrl.startsWith("http")) {
932
- window.location.href = locationUrl;
933
- } else {
934
- navigate(locationUrl);
975
+ let keys;
976
+ if (response instanceof Response) {
977
+ if (response.headers.has("X-Revalidate")) {
978
+ keys = response.headers.get("X-Revalidate").split(",");
979
+ }
980
+ if (response.customBody) data = await response.customBody();
981
+ if (redirectStatusCodes.has(response.status)) {
982
+ const locationUrl = response.headers.get("Location") || "/";
983
+ if (locationUrl.startsWith("http")) {
984
+ window.location.href = locationUrl;
985
+ } else {
986
+ navigate(locationUrl);
987
+ }
935
988
  }
936
989
  } else data = response;
937
- // TODO: handle keys
938
- await revalidate();
990
+ await revalidate(keys);
939
991
  return data;
940
992
  }
941
993
 
@@ -1000,7 +1052,7 @@ function setupNativeEvents(router) {
1000
1052
  }
1001
1053
  }
1002
1054
  function handleFormSubmit(evt) {
1003
- let actionRef = evt.submitter && evt.submitter.getAttribute("formaction") || evt.target.action;
1055
+ let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction") ? evt.submitter.formAction : evt.target.action;
1004
1056
  if (!actionRef) return;
1005
1057
  if (!actionRef.startsWith("action:")) {
1006
1058
  const url = new URL(actionRef);
@@ -1100,7 +1152,7 @@ function HashRouter(props) {
1100
1152
  })(props);
1101
1153
  }
1102
1154
 
1103
- function MemoryRouter(props) {
1155
+ function createMemoryHistory() {
1104
1156
  const entries = ["/"];
1105
1157
  let index = 0;
1106
1158
  const listeners = [];
@@ -1110,13 +1162,13 @@ function MemoryRouter(props) {
1110
1162
  const value = entries[index];
1111
1163
  listeners.forEach(listener => listener(value));
1112
1164
  };
1113
- return createRouter({
1165
+ return {
1114
1166
  get: () => entries[index],
1115
- set({
1167
+ set: ({
1116
1168
  value,
1117
1169
  scroll,
1118
1170
  replace
1119
- }) {
1171
+ }) => {
1120
1172
  if (replace) {
1121
1173
  entries[index] = value;
1122
1174
  } else {
@@ -1127,20 +1179,34 @@ function MemoryRouter(props) {
1127
1179
  scrollToHash(value.split("#")[1] || "", true);
1128
1180
  }
1129
1181
  },
1130
- init(listener) {
1182
+ back: () => {
1183
+ go(-1);
1184
+ },
1185
+ forward: () => {
1186
+ go(1);
1187
+ },
1188
+ go,
1189
+ listen: listener => {
1131
1190
  listeners.push(listener);
1132
1191
  return () => {
1133
1192
  const index = listeners.indexOf(listener);
1134
1193
  listeners.splice(index, 1);
1135
1194
  };
1136
- },
1195
+ }
1196
+ };
1197
+ }
1198
+ function MemoryRouter(props) {
1199
+ const memoryHistory = props.history || createMemoryHistory();
1200
+ return createRouter({
1201
+ get: memoryHistory.get,
1202
+ set: memoryHistory.set,
1203
+ init: memoryHistory.listen,
1137
1204
  utils: {
1138
- go
1205
+ go: memoryHistory.go
1139
1206
  }
1140
1207
  })(props);
1141
1208
  }
1142
1209
 
1143
- /*@refresh skip*/
1144
1210
  const _tmpl$ = /*#__PURE__*/template(`<a>`);
1145
1211
  function A(props) {
1146
1212
  props = mergeProps({
@@ -1255,16 +1321,24 @@ function subFetch(fn) {
1255
1321
  }
1256
1322
 
1257
1323
  function redirect(url, init = 302) {
1258
- let responseInit = init;
1259
- if (typeof responseInit === "number") {
1324
+ let responseInit;
1325
+ let revalidate;
1326
+ if (typeof init === "number") {
1260
1327
  responseInit = {
1261
- status: responseInit
1328
+ status: init
1262
1329
  };
1263
- } else if (typeof responseInit.status === "undefined") {
1264
- responseInit.status = 302;
1330
+ } else {
1331
+ ({
1332
+ revalidate,
1333
+ ...responseInit
1334
+ } = init);
1335
+ if (typeof responseInit.status === "undefined") {
1336
+ responseInit.status = 302;
1337
+ }
1265
1338
  }
1266
1339
  const headers = new Headers(responseInit.headers);
1267
1340
  headers.set("Location", url);
1341
+ revalidate && headers.set("X-Revalidate", revalidate.toString());
1268
1342
  const response = new Response(null, {
1269
1343
  ...responseInit,
1270
1344
  headers: headers
@@ -1272,4 +1346,4 @@ function redirect(url, init = 302) {
1272
1346
  return response;
1273
1347
  }
1274
1348
 
1275
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createRouter, redirect, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
1349
+ export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, redirect, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
@@ -1,4 +1,5 @@
1
1
  import type { JSX } from "solid-js";
2
- import type { RouterProps } from "./components";
2
+ import type { BaseRouterProps } from "./components";
3
3
  export declare function hashParser(str: string): string;
4
- export declare function HashRouter(props: RouterProps): JSX.Element;
4
+ export type HashRouterProps = BaseRouterProps;
5
+ export declare function HashRouter(props: HashRouterProps): JSX.Element;
@@ -1,3 +1,21 @@
1
- import type { RouterProps } from "./components";
1
+ import type { LocationChange } from "../types";
2
+ import type { BaseRouterProps } from "./components";
2
3
  import type { JSX } from "solid-js";
3
- export declare function MemoryRouter(props: RouterProps): JSX.Element;
4
+ export type MemoryHistory = {
5
+ get: () => string;
6
+ set: (change: LocationChange) => void;
7
+ go: (delta: number) => void;
8
+ listen: (listener: (value: string) => void) => () => void;
9
+ };
10
+ export declare function createMemoryHistory(): {
11
+ get: () => string;
12
+ set: ({ value, scroll, replace }: LocationChange) => void;
13
+ back: () => void;
14
+ forward: () => void;
15
+ go: (n: number) => void;
16
+ listen: (listener: (value: string) => void) => () => void;
17
+ };
18
+ export type MemoryRouterProps = BaseRouterProps & {
19
+ history?: MemoryHistory;
20
+ };
21
+ export declare function MemoryRouter(props: MemoryRouterProps): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { createRouter, scrollToHash } from "./createRouter";
2
- export function MemoryRouter(props) {
2
+ export function createMemoryHistory() {
3
3
  const entries = ["/"];
4
4
  let index = 0;
5
5
  const listeners = [];
@@ -9,9 +9,9 @@ export function MemoryRouter(props) {
9
9
  const value = entries[index];
10
10
  listeners.forEach(listener => listener(value));
11
11
  };
12
- return createRouter({
12
+ return {
13
13
  get: () => entries[index],
14
- set({ value, scroll, replace }) {
14
+ set: ({ value, scroll, replace }) => {
15
15
  if (replace) {
16
16
  entries[index] = value;
17
17
  }
@@ -23,15 +23,30 @@ export function MemoryRouter(props) {
23
23
  scrollToHash(value.split("#")[1] || "", true);
24
24
  }
25
25
  },
26
- init(listener) {
26
+ back: () => {
27
+ go(-1);
28
+ },
29
+ forward: () => {
30
+ go(1);
31
+ },
32
+ go,
33
+ listen: (listener) => {
27
34
  listeners.push(listener);
28
35
  return () => {
29
36
  const index = listeners.indexOf(listener);
30
37
  listeners.splice(index, 1);
31
38
  };
32
- },
39
+ }
40
+ };
41
+ }
42
+ export function MemoryRouter(props) {
43
+ const memoryHistory = props.history || createMemoryHistory();
44
+ return createRouter({
45
+ get: memoryHistory.get,
46
+ set: memoryHistory.set,
47
+ init: memoryHistory.listen,
33
48
  utils: {
34
- go
49
+ go: memoryHistory.go
35
50
  }
36
51
  })(props);
37
52
  }
@@ -1,5 +1,6 @@
1
- import type { RouterProps } from "./components";
1
+ import type { BaseRouterProps } from "./components";
2
2
  import type { JSX } from "solid-js";
3
- export declare function Router(props: RouterProps & {
3
+ export type RouterProps = BaseRouterProps & {
4
4
  url?: string;
5
- }): JSX.Element;
5
+ };
6
+ export declare function Router(props: RouterProps): JSX.Element;
@@ -1,5 +1,6 @@
1
- import { type RouterProps } from "./components";
1
+ import { type BaseRouterProps } from "./components";
2
2
  import type { JSX } from "solid-js";
3
- export declare function StaticRouter(props: RouterProps & {
3
+ export type StaticRouterProps = BaseRouterProps & {
4
4
  url?: string;
5
- }): JSX.Element;
5
+ };
6
+ export declare function StaticRouter(props: StaticRouterProps): JSX.Element;
@@ -1,12 +1,12 @@
1
1
  import type { Component, JSX } from "solid-js";
2
2
  import type { MatchFilters, RouteLoadFunc, RouterIntegration, RouteSectionProps } from "../types";
3
- export type RouterProps = {
3
+ export type BaseRouterProps = {
4
4
  base?: string;
5
5
  actionBase?: string;
6
6
  root?: Component<RouteSectionProps>;
7
7
  children?: JSX.Element;
8
8
  };
9
- export declare const createRouterComponent: (router: RouterIntegration) => (props: RouterProps) => JSX.Element;
9
+ export declare const createRouterComponent: (router: RouterIntegration) => (props: BaseRouterProps) => JSX.Element;
10
10
  export type RouteProps<S extends string> = {
11
11
  path?: S | S[];
12
12
  children?: JSX.Element;
@@ -5,6 +5,6 @@ export declare function createRouter(config: {
5
5
  init?: (notify: (value?: string | LocationChange) => void) => () => void;
6
6
  create?: (router: RouterContext) => void;
7
7
  utils?: Partial<RouterUtils>;
8
- }): (props: import("./components").RouterProps) => import("solid-js").JSX.Element;
8
+ }): (props: import("./components").BaseRouterProps) => import("solid-js").JSX.Element;
9
9
  export declare function bindEvent(target: EventTarget, type: string, handler: EventListener): () => void;
10
10
  export declare function scrollToHash(hash: string, fallbackTop?: boolean): void;
@@ -1,7 +1,11 @@
1
1
  export { Route } from "./components";
2
- export type { RouterProps, RouteProps } from "./components";
2
+ export type { BaseRouterProps, RouteProps } from "./components";
3
3
  export { createRouter } from "./createRouter";
4
4
  export { Router } from "./Router";
5
+ export type { RouterProps } from "./Router";
5
6
  export { HashRouter } from "./HashRouter";
6
- export { MemoryRouter } from "./MemoryRouter";
7
+ export type { HashRouterProps } from "./HashRouter";
8
+ export { MemoryRouter, createMemoryHistory } from "./MemoryRouter";
9
+ export type { MemoryRouterProps, MemoryHistory } from "./MemoryRouter";
7
10
  export { StaticRouter } from "./StaticRouter";
11
+ export type { StaticRouterProps } from "./StaticRouter";
@@ -2,5 +2,5 @@ export { Route } from "./components";
2
2
  export { createRouter } from "./createRouter";
3
3
  export { Router } from "./Router";
4
4
  export { HashRouter } from "./HashRouter";
5
- export { MemoryRouter } from "./MemoryRouter";
5
+ export { MemoryRouter, createMemoryHistory } from "./MemoryRouter";
6
6
  export { StaticRouter } from "./StaticRouter";
package/dist/routing.js CHANGED
@@ -202,7 +202,7 @@ export function createRouterContext(integration, getBranches, options = {}) {
202
202
  const [state, setState] = createSignal(source().state);
203
203
  const location = createLocation(reference, state);
204
204
  const referrers = [];
205
- const submissions = createSignal(initFromFlash(location.query));
205
+ const submissions = createSignal(isServer ? initFromFlash() : []);
206
206
  const baseRoute = {
207
207
  pattern: basePath,
208
208
  params: {},
@@ -342,18 +342,9 @@ export function createRouterContext(integration, getBranches, options = {}) {
342
342
  }
343
343
  intent = prevIntent;
344
344
  }
345
- function initFromFlash(params) {
346
- let param = params.form ? JSON.parse(params.form) : null;
347
- if (!param || !param.result)
348
- return [];
349
- const input = new Map(param.entries);
350
- return [
351
- {
352
- url: param.url,
353
- result: param.error ? new Error(param.result) : param.result,
354
- input: input
355
- }
356
- ];
345
+ function initFromFlash() {
346
+ const e = getRequestEvent();
347
+ return e && e.initialSubmission ? [e.initialSubmission] : [];
357
348
  }
358
349
  }
359
350
  export function createRouteContext(router, parent, outlet, match, params) {
@@ -381,6 +372,6 @@ export function createRouteContext(router, parent, outlet, match, params) {
381
372
  component &&
382
373
  component.preload &&
383
374
  component.preload();
384
- load && load({ params, location, intent: intent || "navigate" });
375
+ load && load({ params, location, intent: intent || "initial" });
385
376
  return route;
386
377
  }
package/dist/types.d.ts CHANGED
@@ -3,6 +3,7 @@ declare module "solid-js/web" {
3
3
  interface RequestEvent {
4
4
  response?: Response;
5
5
  routerCache?: Map<any, any>;
6
+ initialSubmission?: Submission<any, any>;
6
7
  }
7
8
  }
8
9
  export type Params = Record<string, string>;
@@ -39,7 +40,7 @@ export interface RouterIntegration {
39
40
  create?: (router: RouterContext) => void;
40
41
  utils?: Partial<RouterUtils>;
41
42
  }
42
- export type Intent = "native" | "navigate" | "preload";
43
+ export type Intent = "initial" | "native" | "navigate" | "preload";
43
44
  export interface RouteLoadFuncArgs {
44
45
  params: Params;
45
46
  location: Location;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.10.0-beta.5",
9
+ "version": "0.10.0-beta.7",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",