@jsenv/navi 0.29.354 → 0.29.355
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/dev/jsenv_navi.js +74 -9
- package/dist/dev/jsenv_navi.js.map +4 -4
- package/dist/jsenv_navi.js +74 -9
- package/dist/jsenv_navi.js.map +4 -4
- package/docs/navigation.md +25 -0
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -13486,10 +13486,12 @@ const createControlValidation = (
|
|
|
13486
13486
|
}
|
|
13487
13487
|
}
|
|
13488
13488
|
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13489
|
+
// The value is about to be read and sent: whoever may correct it gets the
|
|
13490
|
+
// last word before the constraints judge it, so a field is never refused
|
|
13491
|
+
// for something navi knows how to put right. Unless the request comes from
|
|
13492
|
+
// typing — an action run as you type (a search, debounced or not) is not
|
|
13493
|
+
// a commit, and correcting there would eat the space before the next word.
|
|
13494
|
+
if (fromRequestAction && !findEvent(event, "input")) {
|
|
13493
13495
|
applyAutoFix(event);
|
|
13494
13496
|
}
|
|
13495
13497
|
|
|
@@ -26382,10 +26384,27 @@ let isUpdatingRoutesFromUrl = false;
|
|
|
26382
26384
|
* route("/legacy/:id", { redirectRoute: GAME_PAGE, redirectRouteParams: ({ id }) => ({ gameId: id }) });
|
|
26383
26385
|
* route("/:gameId/invite", { redirectRoute: HOME_PAGE, redirectRouteParams: null });
|
|
26384
26386
|
* ```
|
|
26387
|
+
* @param {string[]} [options.dropSearchParams]
|
|
26388
|
+
* Search params this route's address never keeps: read by whoever fetched
|
|
26389
|
+
* the address (a link preview crawler, a cache), never by the app. When this
|
|
26390
|
+
* route's own address arrives carrying one, the navigation goes to the same
|
|
26391
|
+
* address without it, at the door, like a redirection: no history entry, no
|
|
26392
|
+
* route action, no signal written. Every other search param stays as written.
|
|
26393
|
+
*
|
|
26394
|
+
* ```js
|
|
26395
|
+
* // ?v= makes WhatsApp fetch a new preview, the address bar never shows it
|
|
26396
|
+
* route(`/games/:gameId=${gamePageIdSignal}`, { dropSearchParams: ["v"] });
|
|
26397
|
+
* ```
|
|
26385
26398
|
*/
|
|
26386
26399
|
const route = (
|
|
26387
26400
|
pattern,
|
|
26388
|
-
{
|
|
26401
|
+
{
|
|
26402
|
+
searchParams,
|
|
26403
|
+
params,
|
|
26404
|
+
redirectRoute,
|
|
26405
|
+
redirectRouteParams,
|
|
26406
|
+
dropSearchParams,
|
|
26407
|
+
} = {},
|
|
26389
26408
|
) => {
|
|
26390
26409
|
const routePattern = createRoutePattern(pattern, { searchParams, params });
|
|
26391
26410
|
const { cleanPattern } = routePattern;
|
|
@@ -26445,6 +26464,7 @@ const route = (
|
|
|
26445
26464
|
routePattern,
|
|
26446
26465
|
redirectRoute,
|
|
26447
26466
|
redirectRouteParams,
|
|
26467
|
+
dropSearchParams,
|
|
26448
26468
|
setup: null,
|
|
26449
26469
|
updateStatus: null,
|
|
26450
26470
|
cleanup: null,
|
|
@@ -26817,6 +26837,7 @@ const route = (
|
|
|
26817
26837
|
const [publishRouteMutations, observeRouteMutations] = createPubSub();
|
|
26818
26838
|
|
|
26819
26839
|
let redirectingRouteSet = null;
|
|
26840
|
+
let droppingRouteSet = null;
|
|
26820
26841
|
/**
|
|
26821
26842
|
* Where does this url really lead?
|
|
26822
26843
|
*
|
|
@@ -26833,14 +26854,21 @@ let redirectingRouteSet = null;
|
|
|
26833
26854
|
* @returns {string|null} The url to go to instead, or null.
|
|
26834
26855
|
*/
|
|
26835
26856
|
const resolveRouteRedirection = (url) => {
|
|
26836
|
-
if (
|
|
26857
|
+
if (
|
|
26858
|
+
!redirectingRouteSet ||
|
|
26859
|
+
(redirectingRouteSet.size === 0 && droppingRouteSet.size === 0)
|
|
26860
|
+
) {
|
|
26837
26861
|
return null;
|
|
26838
26862
|
}
|
|
26839
26863
|
let urlToResolve = url;
|
|
26840
26864
|
let redirectionUrl = null;
|
|
26841
26865
|
const urlChain = [url];
|
|
26842
26866
|
while (true) {
|
|
26843
|
-
|
|
26867
|
+
// A redirection first: the address it lands on may carry a param its own
|
|
26868
|
+
// route drops, and the next turn removes it there.
|
|
26869
|
+
const nextUrl =
|
|
26870
|
+
resolveRedirectionOnce(urlToResolve) ||
|
|
26871
|
+
dropSearchParamsOnce(urlToResolve);
|
|
26844
26872
|
if (!nextUrl || nextUrl === urlToResolve) {
|
|
26845
26873
|
break;
|
|
26846
26874
|
}
|
|
@@ -26881,6 +26909,35 @@ const resolveRedirectionOnce = (url) => {
|
|
|
26881
26909
|
}
|
|
26882
26910
|
return buildRedirectionUrl(redirectingRoute, redirectingRouteParams);
|
|
26883
26911
|
};
|
|
26912
|
+
// The query is edited as written rather than rebuilt through URLSearchParams,
|
|
26913
|
+
// which would rewrite every param kept ("?weather" into "?weather=", commas
|
|
26914
|
+
// encoded — see extractSearchParams).
|
|
26915
|
+
const dropSearchParamsOnce = (url) => {
|
|
26916
|
+
const urlObject = new URL(url);
|
|
26917
|
+
if (!urlObject.search) {
|
|
26918
|
+
return null;
|
|
26919
|
+
}
|
|
26920
|
+
for (const route of droppingRouteSet) {
|
|
26921
|
+
const { routePattern, dropSearchParams } = getRoutePrivateProperties(route);
|
|
26922
|
+
// exact, for the same reason as a redirection: the addresses below a
|
|
26923
|
+
// trailing slash belong to the routes declared for them
|
|
26924
|
+
if (!routePattern.applyOn(url, { exact: true })) {
|
|
26925
|
+
continue;
|
|
26926
|
+
}
|
|
26927
|
+
const pairs = urlObject.search.slice(1).split("&");
|
|
26928
|
+
const pairsKept = pairs.filter((pair) => {
|
|
26929
|
+
const eqIndex = pair.indexOf("=");
|
|
26930
|
+
const key = eqIndex > -1 ? pair.slice(0, eqIndex) : pair;
|
|
26931
|
+
return !dropSearchParams.includes(decodeURIComponent(key));
|
|
26932
|
+
});
|
|
26933
|
+
if (pairsKept.length === pairs.length) {
|
|
26934
|
+
continue;
|
|
26935
|
+
}
|
|
26936
|
+
urlObject.search = pairsKept.length ? `?${pairsKept.join("&")}` : "";
|
|
26937
|
+
return urlObject.href;
|
|
26938
|
+
}
|
|
26939
|
+
return null;
|
|
26940
|
+
};
|
|
26884
26941
|
const buildRedirectionUrl = (route, urlParams) => {
|
|
26885
26942
|
const { redirectRoute, redirectRouteParams } =
|
|
26886
26943
|
getRoutePrivateProperties(route);
|
|
@@ -26984,8 +27041,13 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
26984
27041
|
// Checked here rather than at declaration: a route may redirect to one
|
|
26985
27042
|
// declared after it, and reading the target then would forbid that order.
|
|
26986
27043
|
redirectingRouteSet = new Set();
|
|
27044
|
+
droppingRouteSet = new Set();
|
|
26987
27045
|
for (const route of routeSet) {
|
|
26988
|
-
const { redirectRoute } =
|
|
27046
|
+
const { redirectRoute, dropSearchParams } =
|
|
27047
|
+
getRoutePrivateProperties(route);
|
|
27048
|
+
if (dropSearchParams && dropSearchParams.length) {
|
|
27049
|
+
droppingRouteSet.add(route);
|
|
27050
|
+
}
|
|
26989
27051
|
if (!redirectRoute) {
|
|
26990
27052
|
continue;
|
|
26991
27053
|
}
|
|
@@ -27271,6 +27333,7 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
27271
27333
|
}
|
|
27272
27334
|
routeSet.clear();
|
|
27273
27335
|
redirectingRouteSet = null;
|
|
27336
|
+
droppingRouteSet = null;
|
|
27274
27337
|
setupRoutesCalled = false;
|
|
27275
27338
|
activeRouteSet = null;
|
|
27276
27339
|
};
|
|
@@ -40809,7 +40872,9 @@ const useControlProps = (props, {
|
|
|
40809
40872
|
requester: control
|
|
40810
40873
|
});
|
|
40811
40874
|
if (dispatched) {
|
|
40812
|
-
|
|
40875
|
+
// Read again: the gate may have corrected the value (see applyAutoFix),
|
|
40876
|
+
// and what the next request is compared with is what went out.
|
|
40877
|
+
lastActionValueRef.current = readControlValue(control);
|
|
40813
40878
|
}
|
|
40814
40879
|
return dispatched;
|
|
40815
40880
|
};
|