@pauloevpr/solid-router 0.16.2-vt.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/LICENSE +21 -0
- package/README.md +1097 -0
- package/dist/components.d.ts +31 -0
- package/dist/components.jsx +40 -0
- package/dist/data/action.d.ts +17 -0
- package/dist/data/action.js +166 -0
- package/dist/data/createAsync.d.ts +32 -0
- package/dist/data/createAsync.js +96 -0
- package/dist/data/events.d.ts +9 -0
- package/dist/data/events.js +123 -0
- package/dist/data/index.d.ts +4 -0
- package/dist/data/index.js +4 -0
- package/dist/data/query.d.ts +23 -0
- package/dist/data/query.js +232 -0
- package/dist/data/response.d.ts +4 -0
- package/dist/data/response.js +42 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2055 -0
- package/dist/index.jsx +7 -0
- package/dist/lifecycle.d.ts +5 -0
- package/dist/lifecycle.js +76 -0
- package/dist/routers/HashRouter.d.ts +9 -0
- package/dist/routers/HashRouter.js +41 -0
- package/dist/routers/MemoryRouter.d.ts +24 -0
- package/dist/routers/MemoryRouter.js +57 -0
- package/dist/routers/Router.d.ts +9 -0
- package/dist/routers/Router.js +45 -0
- package/dist/routers/StaticRouter.d.ts +6 -0
- package/dist/routers/StaticRouter.js +15 -0
- package/dist/routers/components.d.ts +29 -0
- package/dist/routers/components.jsx +122 -0
- package/dist/routers/createRouter.d.ts +10 -0
- package/dist/routers/createRouter.js +41 -0
- package/dist/routers/index.d.ts +11 -0
- package/dist/routers/index.js +6 -0
- package/dist/routing.d.ts +176 -0
- package/dist/routing.js +608 -0
- package/dist/types.d.ts +203 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +193 -0
- package/dist/viewTransitions.d.ts +20 -0
- package/dist/viewTransitions.js +118 -0
- package/package.json +71 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { createSignal, getListener, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
|
|
2
|
+
import { getRequestEvent, isServer } from "solid-js/web";
|
|
3
|
+
import { useNavigate, getIntent, getInPreloadFn } from "../routing.js";
|
|
4
|
+
const LocationHeader = "Location";
|
|
5
|
+
const PRELOAD_TIMEOUT = 5000;
|
|
6
|
+
const CACHE_TIMEOUT = 180000;
|
|
7
|
+
let cacheMap = new Map();
|
|
8
|
+
// cleanup forward/back cache
|
|
9
|
+
if (!isServer) {
|
|
10
|
+
setInterval(() => {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
for (let [k, v] of cacheMap.entries()) {
|
|
13
|
+
if (!v[4].count && now - v[0] > CACHE_TIMEOUT) {
|
|
14
|
+
cacheMap.delete(k);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}, 300000);
|
|
18
|
+
}
|
|
19
|
+
function getCache() {
|
|
20
|
+
if (!isServer)
|
|
21
|
+
return cacheMap;
|
|
22
|
+
const req = getRequestEvent();
|
|
23
|
+
if (!req)
|
|
24
|
+
throw new Error("Cannot find cache context");
|
|
25
|
+
return (req.router || (req.router = {})).cache || (req.router.cache = new Map());
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Revalidates the given cache entry/entries.
|
|
29
|
+
*/
|
|
30
|
+
export function revalidate(key, force = true) {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
// force the cache miss synchronously so a `refetch` in the same tick sees it —
|
|
33
|
+
// startTransition defers its callback to a microtask (#497)
|
|
34
|
+
force && cacheKeyOp(key, entry => (entry[0] = 0));
|
|
35
|
+
return startTransition(() => {
|
|
36
|
+
cacheKeyOp(key, entry => entry[4][1](now)); // retrigger live signals
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function cacheKeyOp(key, fn) {
|
|
40
|
+
key && !Array.isArray(key) && (key = [key]);
|
|
41
|
+
for (let k of cacheMap.keys()) {
|
|
42
|
+
if (key === undefined || matchKey(k, key))
|
|
43
|
+
fn(cacheMap.get(k));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function query(fn, name) {
|
|
47
|
+
// prioritize GET for server functions
|
|
48
|
+
if (fn.GET)
|
|
49
|
+
fn = fn.GET;
|
|
50
|
+
const cachedFn = ((...args) => {
|
|
51
|
+
const cache = getCache();
|
|
52
|
+
const intent = getIntent();
|
|
53
|
+
const inPreloadFn = getInPreloadFn();
|
|
54
|
+
const owner = getOwner();
|
|
55
|
+
const navigate = owner ? useNavigate() : undefined;
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const key = name + hashKey(args);
|
|
58
|
+
let cached = cache.get(key);
|
|
59
|
+
let tracking;
|
|
60
|
+
if (isServer) {
|
|
61
|
+
const e = getRequestEvent();
|
|
62
|
+
if (e) {
|
|
63
|
+
const dataOnly = (e.router || (e.router = {})).dataOnly;
|
|
64
|
+
if (dataOnly) {
|
|
65
|
+
const data = e && (e.router.data || (e.router.data = {}));
|
|
66
|
+
if (data && key in data)
|
|
67
|
+
return data[key];
|
|
68
|
+
if (Array.isArray(dataOnly) && !matchKey(key, dataOnly)) {
|
|
69
|
+
data[key] = undefined;
|
|
70
|
+
return Promise.resolve();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (getListener() && !isServer) {
|
|
76
|
+
tracking = true;
|
|
77
|
+
onCleanup(() => cached[4].count--);
|
|
78
|
+
}
|
|
79
|
+
if (cached &&
|
|
80
|
+
cached[0] &&
|
|
81
|
+
(isServer ||
|
|
82
|
+
intent === "native" ||
|
|
83
|
+
cached[4].count ||
|
|
84
|
+
Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
|
|
85
|
+
if (tracking) {
|
|
86
|
+
cached[4].count++;
|
|
87
|
+
cached[4][0](); // track
|
|
88
|
+
}
|
|
89
|
+
if (cached[3] === "preload" && intent !== "preload") {
|
|
90
|
+
cached[0] = now;
|
|
91
|
+
}
|
|
92
|
+
let res = cached[1];
|
|
93
|
+
if (intent !== "preload") {
|
|
94
|
+
res =
|
|
95
|
+
"then" in cached[1]
|
|
96
|
+
? cached[1].then(handleResponse(false), handleResponse(true))
|
|
97
|
+
: handleResponse(false)(cached[1]);
|
|
98
|
+
!isServer && intent === "navigate" && startTransition(() => cached[4][1](cached[0])); // update version
|
|
99
|
+
}
|
|
100
|
+
inPreloadFn && "then" in res && res.catch(() => { });
|
|
101
|
+
return res;
|
|
102
|
+
}
|
|
103
|
+
let res;
|
|
104
|
+
if (!isServer && sharedConfig.has && sharedConfig.has(key)) {
|
|
105
|
+
res = sharedConfig.load(key); // hydrating
|
|
106
|
+
// @ts-ignore at least until we add a delete method to sharedConfig
|
|
107
|
+
delete globalThis._$HY.r[key];
|
|
108
|
+
}
|
|
109
|
+
else
|
|
110
|
+
res = fn(...args);
|
|
111
|
+
if (cached) {
|
|
112
|
+
cached[0] = now;
|
|
113
|
+
cached[1] = res;
|
|
114
|
+
cached[3] = intent;
|
|
115
|
+
!isServer && intent === "navigate" && startTransition(() => cached[4][1](cached[0])); // update version
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
cache.set(key, (cached = [now, res, , intent, createSignal(now)]));
|
|
119
|
+
cached[4].count = 0;
|
|
120
|
+
}
|
|
121
|
+
if (tracking) {
|
|
122
|
+
cached[4].count++;
|
|
123
|
+
cached[4][0](); // track
|
|
124
|
+
}
|
|
125
|
+
if (isServer) {
|
|
126
|
+
const e = getRequestEvent();
|
|
127
|
+
if (e && e.router.dataOnly)
|
|
128
|
+
return (e.router.data[key] = res);
|
|
129
|
+
}
|
|
130
|
+
if (intent !== "preload") {
|
|
131
|
+
res =
|
|
132
|
+
"then" in res
|
|
133
|
+
? res.then(handleResponse(false), handleResponse(true))
|
|
134
|
+
: handleResponse(false)(res);
|
|
135
|
+
}
|
|
136
|
+
inPreloadFn && "then" in res && res.catch(() => { });
|
|
137
|
+
// serialize on server
|
|
138
|
+
if (isServer &&
|
|
139
|
+
sharedConfig.context &&
|
|
140
|
+
sharedConfig.context.async &&
|
|
141
|
+
!sharedConfig.context.noHydrate) {
|
|
142
|
+
const e = getRequestEvent();
|
|
143
|
+
(!e || !e.serverOnly) && sharedConfig.context.serialize(key, res);
|
|
144
|
+
}
|
|
145
|
+
return res;
|
|
146
|
+
function handleResponse(error) {
|
|
147
|
+
return async (v) => {
|
|
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
|
+
}
|
|
158
|
+
const url = v.headers.get(LocationHeader);
|
|
159
|
+
if (url !== null) {
|
|
160
|
+
// client + server relative redirect
|
|
161
|
+
if (navigate && url.startsWith("/"))
|
|
162
|
+
startTransition(() => {
|
|
163
|
+
navigate(url, { replace: true });
|
|
164
|
+
});
|
|
165
|
+
else if (!isServer)
|
|
166
|
+
window.location.href = url;
|
|
167
|
+
else if (e)
|
|
168
|
+
e.response.status = 302;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (v.customBody)
|
|
172
|
+
v = await v.customBody();
|
|
173
|
+
}
|
|
174
|
+
if (error)
|
|
175
|
+
throw v;
|
|
176
|
+
cached[2] = v;
|
|
177
|
+
return v;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
cachedFn.keyFor = (...args) => name + hashKey(args);
|
|
182
|
+
cachedFn.key = name;
|
|
183
|
+
return cachedFn;
|
|
184
|
+
}
|
|
185
|
+
query.get = (key) => {
|
|
186
|
+
const cached = getCache().get(key);
|
|
187
|
+
return cached[2];
|
|
188
|
+
};
|
|
189
|
+
query.set = (key, value) => {
|
|
190
|
+
const cache = getCache();
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
let cached = cache.get(key);
|
|
193
|
+
if (cached) {
|
|
194
|
+
cached[0] = now;
|
|
195
|
+
cached[1] = Promise.resolve(value);
|
|
196
|
+
cached[2] = value;
|
|
197
|
+
cached[3] = "preload";
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
cache.set(key, (cached = [now, Promise.resolve(value), value, "preload", createSignal(now)]));
|
|
201
|
+
cached[4].count = 0;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
query.delete = (key) => getCache().delete(key);
|
|
205
|
+
query.clear = () => getCache().clear();
|
|
206
|
+
/** @deprecated use query instead */
|
|
207
|
+
export const cache = query;
|
|
208
|
+
function matchKey(key, keys) {
|
|
209
|
+
for (let k of keys) {
|
|
210
|
+
if (k && key.startsWith(k))
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
// Modified from the amazing Tanstack Query library (MIT)
|
|
216
|
+
// https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L168
|
|
217
|
+
export function hashKey(args) {
|
|
218
|
+
return JSON.stringify(args, (_, val) => isPlainObject(val)
|
|
219
|
+
? Object.keys(val)
|
|
220
|
+
.sort()
|
|
221
|
+
.reduce((result, key) => {
|
|
222
|
+
result[key] = val[key];
|
|
223
|
+
return result;
|
|
224
|
+
}, {})
|
|
225
|
+
: val);
|
|
226
|
+
}
|
|
227
|
+
function isPlainObject(obj) {
|
|
228
|
+
let proto;
|
|
229
|
+
return (obj != null &&
|
|
230
|
+
typeof obj === "object" &&
|
|
231
|
+
(!(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype));
|
|
232
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RouterResponseInit, CustomResponse } from "../types.js";
|
|
2
|
+
export declare function redirect(url: string, init?: number | RouterResponseInit): CustomResponse<never>;
|
|
3
|
+
export declare function reload(init?: RouterResponseInit): CustomResponse<never>;
|
|
4
|
+
export declare function json<T>(data: T, init?: RouterResponseInit): CustomResponse<T>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function redirect(url, init = 302) {
|
|
2
|
+
let responseInit;
|
|
3
|
+
let revalidate;
|
|
4
|
+
if (typeof init === "number") {
|
|
5
|
+
responseInit = { status: init };
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
({ revalidate, ...responseInit } = init);
|
|
9
|
+
if (typeof responseInit.status === "undefined") {
|
|
10
|
+
responseInit.status = 302;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const headers = new Headers(responseInit.headers);
|
|
14
|
+
headers.set("Location", url);
|
|
15
|
+
revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
|
|
16
|
+
const response = new Response(null, {
|
|
17
|
+
...responseInit,
|
|
18
|
+
headers: headers
|
|
19
|
+
});
|
|
20
|
+
return response;
|
|
21
|
+
}
|
|
22
|
+
export function reload(init = {}) {
|
|
23
|
+
const { revalidate, ...responseInit } = init;
|
|
24
|
+
const headers = new Headers(responseInit.headers);
|
|
25
|
+
revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
|
|
26
|
+
return new Response(null, {
|
|
27
|
+
...responseInit,
|
|
28
|
+
headers
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function json(data, init = {}) {
|
|
32
|
+
const { revalidate, ...responseInit } = init;
|
|
33
|
+
const headers = new Headers(responseInit.headers);
|
|
34
|
+
revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
|
|
35
|
+
headers.set("Content-Type", "application/json");
|
|
36
|
+
const response = new Response(JSON.stringify(data), {
|
|
37
|
+
...responseInit,
|
|
38
|
+
headers
|
|
39
|
+
});
|
|
40
|
+
response.customBody = () => data;
|
|
41
|
+
return response;
|
|
42
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./routers/index.js";
|
|
2
|
+
export * from "./components.jsx";
|
|
3
|
+
export * from "./lifecycle.js";
|
|
4
|
+
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, usePreloadRoute, RouterContextObj as RouterContext } from "./routing.js";
|
|
5
|
+
export { mergeSearchString as _mergeSearchString } from "./utils.js";
|
|
6
|
+
export * from "./data/index.js";
|
|
7
|
+
export { viewTransitionSource, viewTransitionTarget, type ViewTransitionAnimation, type ViewTransitionOptions, type ViewTransitionSourceOptions, type ViewTransitionTargetOptions } from "./viewTransitions.js";
|
|
8
|
+
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";
|