@solidjs/router 2.0.0-next.12 → 2.0.0-next.14
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/data/action.js +5 -1
- package/dist/data/events.js +4 -0
- package/dist/data/query.js +20 -5
- package/dist/index.js +41 -7
- package/dist/routing.js +16 -5
- package/dist/types.d.ts +7 -0
- package/package.json +6 -6
package/dist/data/action.js
CHANGED
|
@@ -330,7 +330,11 @@ function applyResponseMetadata(metadata, navigate, flightData) {
|
|
|
330
330
|
cacheKeyOp(keys, entry => (entry[0] = 0));
|
|
331
331
|
// set cache
|
|
332
332
|
flightData && Object.keys(flightData).forEach(k => query.set(k, flightData[k]));
|
|
333
|
-
// trigger revalidation
|
|
333
|
+
// trigger revalidation inside the same transition as the navigation, so
|
|
334
|
+
// the redirect commits atomically with fresh data: surviving consumers
|
|
335
|
+
// (shared layouts) the flight payload didn't seed refetch and hold the
|
|
336
|
+
// commit rather than painting stale and updating after. Seeded entries
|
|
337
|
+
// are fresh again by now, so the sweep re-reads them from cache.
|
|
334
338
|
revalidate(keys, false);
|
|
335
339
|
}
|
|
336
340
|
async function handleResponse(response, error, navigate, metadataHandled) {
|
package/dist/data/events.js
CHANGED
|
@@ -35,6 +35,10 @@ export function setupNativeEvents({ preload = true, explicitLinks = false, actio
|
|
|
35
35
|
if (a.hasAttribute("download") || (rel && rel.includes("external")))
|
|
36
36
|
return;
|
|
37
37
|
const url = svg ? new URL(href, document.baseURI) : new URL(href);
|
|
38
|
+
// Skip non-http(s) schemes (blob:, mailto:, tel:, data:, ...). blob: URLs
|
|
39
|
+
// inherit the page origin, so the origin check below won't reject them. #382
|
|
40
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
41
|
+
return;
|
|
38
42
|
if (url.origin !== window.location.origin ||
|
|
39
43
|
(basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())))
|
|
40
44
|
return;
|
package/dist/data/query.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSignal, getObserver, getOwner, onCleanup, sharedConfig } from "solid-js";
|
|
2
|
-
import { getRequestEvent, isResponseEnvelope, isServer } from "@solidjs/web";
|
|
2
|
+
import { getRequestEvent, isResponseEnvelope, isServer, REVALIDATE_HEADER } from "@solidjs/web";
|
|
3
3
|
import { GET, decodeResponse, getServerFunctionMetadata, isServerFunction } from "@solidjs/web/server-functions";
|
|
4
|
-
import {
|
|
4
|
+
import { useRouter, getIntent, getInPreloadFn } from "../routing.js";
|
|
5
5
|
const LocationHeader = "Location";
|
|
6
6
|
const PRELOAD_TIMEOUT = 5000;
|
|
7
7
|
const CACHE_TIMEOUT = 180000;
|
|
@@ -58,7 +58,8 @@ export function query(fn, name) {
|
|
|
58
58
|
const intent = getIntent();
|
|
59
59
|
const inPreloadFn = getInPreloadFn();
|
|
60
60
|
const owner = getOwner();
|
|
61
|
-
const
|
|
61
|
+
const router = owner ? useRouter() : undefined;
|
|
62
|
+
const navigate = router && router.navigatorFactory();
|
|
62
63
|
const now = Date.now();
|
|
63
64
|
const key = name + hashKey(args);
|
|
64
65
|
let cached = cache.get(key);
|
|
@@ -171,14 +172,28 @@ export function query(fn, name) {
|
|
|
171
172
|
}
|
|
172
173
|
const url = v.headers.get(LocationHeader);
|
|
173
174
|
if (url !== null) {
|
|
175
|
+
// invalidate the redirect's revalidation keys before navigating so
|
|
176
|
+
// the destination's preloads see the miss and fetch fresh (#580 thread)
|
|
177
|
+
const keys = !isServer && v.headers.get(REVALIDATE_HEADER)?.split(",");
|
|
178
|
+
keys && cacheKeyOp(keys, entry => (entry[0] = 0));
|
|
174
179
|
// client + server relative redirect
|
|
175
|
-
|
|
180
|
+
const soft = navigate && url.startsWith("/");
|
|
181
|
+
if (soft)
|
|
176
182
|
navigate(url, { replace: true });
|
|
177
183
|
else if (!isServer)
|
|
178
184
|
window.location.href = url;
|
|
179
185
|
else if (e)
|
|
180
186
|
e.response.status = 302;
|
|
181
|
-
|
|
187
|
+
// sweep the live signals inside the same transition as the
|
|
188
|
+
// navigation: surviving consumers (shared layouts) refetch and
|
|
189
|
+
// hold the commit, so the destination never paints stale data
|
|
190
|
+
keys && revalidate(keys, false);
|
|
191
|
+
// Hold the read pending on the client: the navigation unmounts this
|
|
192
|
+
// consumer, and resolving `undefined` instead hands a missing value
|
|
193
|
+
// to whatever renders before the transition commits (a downstream
|
|
194
|
+
// `.map()` crashes). The server keeps resolving — its render can't
|
|
195
|
+
// await a navigation and the 302 discards the body anyway.
|
|
196
|
+
return isServer ? undefined : new Promise(() => { });
|
|
182
197
|
}
|
|
183
198
|
if (hasEnveloped)
|
|
184
199
|
v = enveloped;
|
package/dist/index.js
CHANGED
|
@@ -336,6 +336,9 @@ function setupNativeEvents({
|
|
|
336
336
|
const rel = (a.getAttribute("rel") || "").split(/\s+/);
|
|
337
337
|
if (a.hasAttribute("download") || rel && rel.includes("external")) return;
|
|
338
338
|
const url = svg ? new URL(href, document.baseURI) : new URL(href);
|
|
339
|
+
// Skip non-http(s) schemes (blob:, mailto:, tel:, data:, ...). blob: URLs
|
|
340
|
+
// inherit the page origin, so the origin check below won't reject them. #382
|
|
341
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return;
|
|
339
342
|
if (url.origin !== window.location.origin || basePath && url.pathname && !url.pathname.toLowerCase().startsWith(basePath.toLowerCase())) return;
|
|
340
343
|
return [a, url];
|
|
341
344
|
}
|
|
@@ -1191,7 +1194,6 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
1191
1194
|
}
|
|
1192
1195
|
// typed path proxy nodes coerce to their href
|
|
1193
1196
|
if (typeof to !== "string") to = to.toString();
|
|
1194
|
-
const queryOnly = !to || to[0] === "?";
|
|
1195
1197
|
const {
|
|
1196
1198
|
replace,
|
|
1197
1199
|
resolve,
|
|
@@ -1199,11 +1201,21 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
1199
1201
|
state: nextState
|
|
1200
1202
|
} = {
|
|
1201
1203
|
replace: false,
|
|
1202
|
-
resolve:
|
|
1204
|
+
resolve: true,
|
|
1203
1205
|
scroll: true,
|
|
1204
1206
|
...options
|
|
1205
1207
|
};
|
|
1206
|
-
|
|
1208
|
+
|
|
1209
|
+
// A string means what the same string means as an href. Leading "/"
|
|
1210
|
+
// stays base-prefixed; anything else resolves URL-style against the
|
|
1211
|
+
// current location — `new URL` collapses `..` and handles `?`/`#`-only
|
|
1212
|
+
// references natively (#502). A cross-origin result (scheme or
|
|
1213
|
+
// protocol-relative input) falls through as unroutable.
|
|
1214
|
+
let resolvedTo;
|
|
1215
|
+
if (!resolve) resolvedTo = resolvePath((!to || to[0] === "?") && location.pathname || "", to);else if (to[0] === "/") resolvedTo = route.resolvePath(to);else {
|
|
1216
|
+
const url = new URL(to, mockBase + location.pathname + location.search + location.hash);
|
|
1217
|
+
resolvedTo = url.origin === mockBase ? url.pathname + url.search + url.hash : undefined;
|
|
1218
|
+
}
|
|
1207
1219
|
if (resolvedTo === undefined) {
|
|
1208
1220
|
throw new Error(`Path '${to}' is not a routable path`);
|
|
1209
1221
|
} else if (referrers.length >= MAX_REDIRECTS) {
|
|
@@ -2174,7 +2186,8 @@ function query(fn, name) {
|
|
|
2174
2186
|
const intent = getIntent();
|
|
2175
2187
|
const inPreloadFn = getInPreloadFn();
|
|
2176
2188
|
const owner = getOwner();
|
|
2177
|
-
const
|
|
2189
|
+
const router = owner ? useRouter() : undefined;
|
|
2190
|
+
const navigate = router && router.navigatorFactory();
|
|
2178
2191
|
const now = Date.now();
|
|
2179
2192
|
const key = name + hashKey(args);
|
|
2180
2193
|
let cached = cache.get(key);
|
|
@@ -2267,11 +2280,28 @@ function query(fn, name) {
|
|
|
2267
2280
|
}
|
|
2268
2281
|
const url = v.headers.get(LocationHeader);
|
|
2269
2282
|
if (url !== null) {
|
|
2283
|
+
// invalidate the redirect's revalidation keys before navigating so
|
|
2284
|
+
// the destination's preloads see the miss and fetch fresh (#580 thread)
|
|
2285
|
+
const keys = !isServer && v.headers.get(REVALIDATE_HEADER)?.split(",");
|
|
2286
|
+
keys && cacheKeyOp(keys, entry => entry[0] = 0);
|
|
2287
|
+
|
|
2270
2288
|
// client + server relative redirect
|
|
2271
|
-
|
|
2289
|
+
const soft = navigate && url.startsWith("/");
|
|
2290
|
+
if (soft) navigate(url, {
|
|
2272
2291
|
replace: true
|
|
2273
2292
|
});else if (!isServer) window.location.href = url;else if (e) e.response.status = 302;
|
|
2274
|
-
|
|
2293
|
+
|
|
2294
|
+
// sweep the live signals inside the same transition as the
|
|
2295
|
+
// navigation: surviving consumers (shared layouts) refetch and
|
|
2296
|
+
// hold the commit, so the destination never paints stale data
|
|
2297
|
+
keys && revalidate(keys, false);
|
|
2298
|
+
|
|
2299
|
+
// Hold the read pending on the client: the navigation unmounts this
|
|
2300
|
+
// consumer, and resolving `undefined` instead hands a missing value
|
|
2301
|
+
// to whatever renders before the transition commits (a downstream
|
|
2302
|
+
// `.map()` crashes). The server keeps resolving — its render can't
|
|
2303
|
+
// await a navigation and the 302 discards the body anyway.
|
|
2304
|
+
return isServer ? undefined : new Promise(() => {});
|
|
2275
2305
|
}
|
|
2276
2306
|
if (hasEnveloped) v = enveloped;else if (v.body) {
|
|
2277
2307
|
// responses the transport hands over whole (revalidation) carry a
|
|
@@ -2660,7 +2690,11 @@ function applyResponseMetadata(metadata, navigate, flightData) {
|
|
|
2660
2690
|
cacheKeyOp(keys, entry => entry[0] = 0);
|
|
2661
2691
|
// set cache
|
|
2662
2692
|
flightData && Object.keys(flightData).forEach(k => query.set(k, flightData[k]));
|
|
2663
|
-
// trigger revalidation
|
|
2693
|
+
// trigger revalidation inside the same transition as the navigation, so
|
|
2694
|
+
// the redirect commits atomically with fresh data: surviving consumers
|
|
2695
|
+
// (shared layouts) the flight payload didn't seed refetch and hold the
|
|
2696
|
+
// commit rather than painting stale and updating after. Seeded entries
|
|
2697
|
+
// are fresh again by now, so the sweep re-reads them from cache.
|
|
2664
2698
|
revalidate(keys, false);
|
|
2665
2699
|
}
|
|
2666
2700
|
async function handleResponse(response, error, navigate, metadataHandled) {
|
package/dist/routing.js
CHANGED
|
@@ -650,16 +650,27 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
650
650
|
// typed path proxy nodes coerce to their href
|
|
651
651
|
if (typeof to !== "string")
|
|
652
652
|
to = to.toString();
|
|
653
|
-
const queryOnly = !to || to[0] === "?";
|
|
654
653
|
const { replace, resolve, scroll, state: nextState } = {
|
|
655
654
|
replace: false,
|
|
656
|
-
resolve:
|
|
655
|
+
resolve: true,
|
|
657
656
|
scroll: true,
|
|
658
657
|
...options
|
|
659
658
|
};
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
659
|
+
// A string means what the same string means as an href. Leading "/"
|
|
660
|
+
// stays base-prefixed; anything else resolves URL-style against the
|
|
661
|
+
// current location — `new URL` collapses `..` and handles `?`/`#`-only
|
|
662
|
+
// references natively (#502). A cross-origin result (scheme or
|
|
663
|
+
// protocol-relative input) falls through as unroutable.
|
|
664
|
+
let resolvedTo;
|
|
665
|
+
if (!resolve)
|
|
666
|
+
resolvedTo = resolvePath(((!to || to[0] === "?") && location.pathname) || "", to);
|
|
667
|
+
else if (to[0] === "/")
|
|
668
|
+
resolvedTo = route.resolvePath(to);
|
|
669
|
+
else {
|
|
670
|
+
const url = new URL(to, mockBase + location.pathname + location.search + location.hash);
|
|
671
|
+
resolvedTo =
|
|
672
|
+
url.origin === mockBase ? url.pathname + url.search + url.hash : undefined;
|
|
673
|
+
}
|
|
663
674
|
if (resolvedTo === undefined) {
|
|
664
675
|
throw new Error(`Path '${to}' is not a routable path`);
|
|
665
676
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ declare module "@solidjs/web" {
|
|
|
6
6
|
status?: number;
|
|
7
7
|
statusText?: string;
|
|
8
8
|
headers: Headers;
|
|
9
|
+
committed?: boolean;
|
|
9
10
|
};
|
|
10
11
|
router?: {
|
|
11
12
|
matches?: OutputMatch[];
|
|
@@ -74,6 +75,12 @@ export interface Location<S = unknown> extends Path {
|
|
|
74
75
|
key: string;
|
|
75
76
|
}
|
|
76
77
|
export interface NavigateOptions<S = unknown> {
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the target like an href: leading-`/` paths are prefixed with the
|
|
80
|
+
* router base, anything else resolves URL-style against the current
|
|
81
|
+
* location (`..` collapses, `?`/`#`-only strings keep the path). `false`
|
|
82
|
+
* takes the string as the final path. Defaults to `true`.
|
|
83
|
+
*/
|
|
77
84
|
resolve: boolean;
|
|
78
85
|
replace: boolean;
|
|
79
86
|
scroll: boolean;
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"Ryan Turnquist"
|
|
7
7
|
],
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"version": "2.0.0-next.
|
|
9
|
+
"version": "2.0.0-next.14",
|
|
10
10
|
"homepage": "https://github.com/solidjs/solid-router#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@solidjs/web": "2.0.0-beta.
|
|
34
|
+
"@solidjs/web": "2.0.0-beta.30",
|
|
35
35
|
"@babel/core": "^7.26.0",
|
|
36
36
|
"@babel/preset-typescript": "^7.26.0",
|
|
37
37
|
"@changesets/cli": "^2.27.10",
|
|
@@ -40,19 +40,19 @@
|
|
|
40
40
|
"@rollup/plugin-terser": "0.4.4",
|
|
41
41
|
"@types/jest": "^29.5.14",
|
|
42
42
|
"@types/node": "^22.10.0",
|
|
43
|
-
"babel-preset-solid": "2.0.0-beta.
|
|
43
|
+
"babel-preset-solid": "2.0.0-beta.30",
|
|
44
44
|
"jsdom": "^25.0.1",
|
|
45
45
|
"prettier": "^3.4.1",
|
|
46
46
|
"rollup": "^4.27.4",
|
|
47
|
-
"solid-js": "2.0.0-beta.
|
|
47
|
+
"solid-js": "2.0.0-beta.30",
|
|
48
48
|
"typescript": "^5.7.2",
|
|
49
49
|
"vite": "^6.0.0",
|
|
50
50
|
"vite-plugin-solid": "3.0.0-next.10",
|
|
51
51
|
"vitest": "^2.1.6"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@solidjs/web": ">=2.0.0-beta.
|
|
55
|
-
"solid-js": ">=2.0.0-beta.
|
|
54
|
+
"@solidjs/web": ">=2.0.0-beta.30 <2.0.0-experimental.0",
|
|
55
|
+
"solid-js": ">=2.0.0-beta.30 <2.0.0-experimental.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "rm -rf dist && tsc && rollup -c",
|