@jsenv/navi 0.16.7 → 0.16.8
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/jsenv_navi.js +176 -122
- package/dist/jsenv_navi.js.map +3 -5
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -7552,6 +7552,62 @@ const useUITransitionContentId = value => {
|
|
|
7552
7552
|
*/
|
|
7553
7553
|
|
|
7554
7554
|
|
|
7555
|
+
// Raw URL part functionality for bypassing encoding
|
|
7556
|
+
const rawUrlPartSymbol = Symbol("raw_url_part");
|
|
7557
|
+
const rawUrlPart = (value) => {
|
|
7558
|
+
return {
|
|
7559
|
+
[rawUrlPartSymbol]: true,
|
|
7560
|
+
value,
|
|
7561
|
+
};
|
|
7562
|
+
};
|
|
7563
|
+
|
|
7564
|
+
/**
|
|
7565
|
+
* Encode parameter values for URL usage, with special handling for raw URL parts.
|
|
7566
|
+
* When a parameter is wrapped with rawUrlPart(), it bypasses encoding and is
|
|
7567
|
+
* inserted as-is into the URL.
|
|
7568
|
+
*/
|
|
7569
|
+
const encodeParamValue = (value, isWildcard = false) => {
|
|
7570
|
+
if (value && value[rawUrlPartSymbol]) {
|
|
7571
|
+
return value.value;
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7574
|
+
if (isWildcard) {
|
|
7575
|
+
// For wildcards, only encode characters that are invalid in URL paths,
|
|
7576
|
+
// but preserve slashes as they are path separators
|
|
7577
|
+
return value
|
|
7578
|
+
? value.replace(/[^a-zA-Z0-9\-._~!$&'()*+,;=:@/]/g, (char) => {
|
|
7579
|
+
return encodeURIComponent(char);
|
|
7580
|
+
})
|
|
7581
|
+
: value;
|
|
7582
|
+
}
|
|
7583
|
+
|
|
7584
|
+
// For named parameters and search params, encode everything including slashes
|
|
7585
|
+
return encodeURIComponent(value);
|
|
7586
|
+
};
|
|
7587
|
+
|
|
7588
|
+
/**
|
|
7589
|
+
* Build query string from parameters, respecting rawUrlPart values
|
|
7590
|
+
*/
|
|
7591
|
+
const buildQueryString = (params) => {
|
|
7592
|
+
const searchParamPairs = [];
|
|
7593
|
+
|
|
7594
|
+
for (const [key, value] of Object.entries(params)) {
|
|
7595
|
+
if (value !== undefined && value !== null) {
|
|
7596
|
+
const encodedKey = encodeURIComponent(key);
|
|
7597
|
+
|
|
7598
|
+
// Handle boolean values - if true, just add the key without value
|
|
7599
|
+
if (value === true || value === "") {
|
|
7600
|
+
searchParamPairs.push(encodedKey);
|
|
7601
|
+
} else {
|
|
7602
|
+
const encodedValue = encodeParamValue(value, false); // Search params encode slashes
|
|
7603
|
+
searchParamPairs.push(`${encodedKey}=${encodedValue}`);
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
}
|
|
7607
|
+
|
|
7608
|
+
return searchParamPairs.join("&");
|
|
7609
|
+
};
|
|
7610
|
+
|
|
7555
7611
|
// Base URL management
|
|
7556
7612
|
let baseFileUrl;
|
|
7557
7613
|
let baseUrl;
|
|
@@ -7668,7 +7724,7 @@ const createRoutePattern = (pattern) => {
|
|
|
7668
7724
|
};
|
|
7669
7725
|
|
|
7670
7726
|
const buildUrl = (params = {}) => {
|
|
7671
|
-
return buildUrlFromPattern(parsedPattern, params);
|
|
7727
|
+
return buildUrlFromPattern(parsedPattern, params, pattern);
|
|
7672
7728
|
};
|
|
7673
7729
|
|
|
7674
7730
|
const resolveParams = (providedParams = {}) => {
|
|
@@ -8001,7 +8057,9 @@ const createRoutePattern = (pattern) => {
|
|
|
8001
8057
|
const finalParams = removeDefaultValues(resolvedParams);
|
|
8002
8058
|
const currentUrl = buildUrlFromPattern(
|
|
8003
8059
|
parsedPattern,
|
|
8004
|
-
finalParams
|
|
8060
|
+
finalParams,
|
|
8061
|
+
pattern,
|
|
8062
|
+
);
|
|
8005
8063
|
if (currentUrl.length < childUrl.length) {
|
|
8006
8064
|
return currentUrl;
|
|
8007
8065
|
}
|
|
@@ -8105,7 +8163,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8105
8163
|
filteredPattern.trailingSlash = false;
|
|
8106
8164
|
}
|
|
8107
8165
|
|
|
8108
|
-
return buildUrlFromPattern(filteredPattern, finalParams);
|
|
8166
|
+
return buildUrlFromPattern(filteredPattern, finalParams, pattern);
|
|
8109
8167
|
};
|
|
8110
8168
|
|
|
8111
8169
|
/**
|
|
@@ -8160,6 +8218,40 @@ const createRoutePattern = (pattern) => {
|
|
|
8160
8218
|
);
|
|
8161
8219
|
|
|
8162
8220
|
if (optimizedParentUrl) {
|
|
8221
|
+
// Before returning optimized parent URL, check if we need to inherit parameters
|
|
8222
|
+
// from our ancestors that the parent route might not inherit on its own
|
|
8223
|
+
const parentFinalParams = { ...resolvedParams };
|
|
8224
|
+
|
|
8225
|
+
// Remove params that belong to current route (they're at defaults anyway)
|
|
8226
|
+
for (const conn of connections) {
|
|
8227
|
+
delete parentFinalParams[conn.paramName];
|
|
8228
|
+
}
|
|
8229
|
+
|
|
8230
|
+
// Inherit from all ancestor routes, not just immediate parent
|
|
8231
|
+
inheritParentParameters(parentFinalParams, relationships);
|
|
8232
|
+
|
|
8233
|
+
// If we inherited any parameters, add them to the parent URL
|
|
8234
|
+
const extraParamEntries = Object.entries(parentFinalParams).filter(
|
|
8235
|
+
([key, value]) => {
|
|
8236
|
+
// Only include params not handled by parent route
|
|
8237
|
+
const isParentParam = parentPatternObj.connections.some(
|
|
8238
|
+
(conn) => conn.paramName === key,
|
|
8239
|
+
);
|
|
8240
|
+
return !isParentParam && value !== undefined;
|
|
8241
|
+
},
|
|
8242
|
+
);
|
|
8243
|
+
|
|
8244
|
+
if (extraParamEntries.length > 0) {
|
|
8245
|
+
const queryString = buildQueryString(
|
|
8246
|
+
Object.fromEntries(extraParamEntries),
|
|
8247
|
+
);
|
|
8248
|
+
return (
|
|
8249
|
+
optimizedParentUrl +
|
|
8250
|
+
(optimizedParentUrl.includes("?") ? "&" : "?") +
|
|
8251
|
+
queryString
|
|
8252
|
+
);
|
|
8253
|
+
}
|
|
8254
|
+
|
|
8163
8255
|
return optimizedParentUrl;
|
|
8164
8256
|
}
|
|
8165
8257
|
}
|
|
@@ -8591,58 +8683,98 @@ const extractSearchParams = (urlObj, connections = []) => {
|
|
|
8591
8683
|
};
|
|
8592
8684
|
|
|
8593
8685
|
/**
|
|
8594
|
-
*
|
|
8595
|
-
* This detects when a parameter doesn't match the current route's parameters
|
|
8596
|
-
* but the route has literal segments that might correspond to parent route parameters
|
|
8686
|
+
* Build query parameters respecting hierarchical order from ancestor patterns
|
|
8597
8687
|
*/
|
|
8598
|
-
const
|
|
8599
|
-
paramName,
|
|
8600
|
-
paramValue,
|
|
8688
|
+
const buildHierarchicalQueryParams = (
|
|
8601
8689
|
parsedPattern,
|
|
8602
|
-
|
|
8603
|
-
|
|
8690
|
+
params,
|
|
8691
|
+
originalPattern,
|
|
8604
8692
|
) => {
|
|
8605
|
-
|
|
8606
|
-
const
|
|
8607
|
-
|
|
8693
|
+
const queryParams = {};
|
|
8694
|
+
const processedParams = new Set();
|
|
8695
|
+
|
|
8696
|
+
// Get relationships for this pattern
|
|
8697
|
+
const relationships = patternRelationships.get(originalPattern);
|
|
8698
|
+
const parentPatterns = relationships?.parentPatterns || [];
|
|
8699
|
+
|
|
8700
|
+
// Step 1: Add query parameters from ancestor patterns (oldest to newest)
|
|
8701
|
+
// This ensures ancestor parameters come first in their declaration order
|
|
8702
|
+
const ancestorPatterns = parentPatterns; // Process in order: root ancestor first, then immediate parent
|
|
8703
|
+
|
|
8704
|
+
for (const ancestorPatternObj of ancestorPatterns) {
|
|
8705
|
+
if (ancestorPatternObj.pattern?.queryParams) {
|
|
8706
|
+
|
|
8707
|
+
for (const queryParam of ancestorPatternObj.pattern.queryParams) {
|
|
8708
|
+
const paramName = queryParam.name;
|
|
8709
|
+
if (
|
|
8710
|
+
params[paramName] !== undefined &&
|
|
8711
|
+
!processedParams.has(paramName)
|
|
8712
|
+
) {
|
|
8713
|
+
queryParams[paramName] = params[paramName];
|
|
8714
|
+
processedParams.add(paramName);
|
|
8715
|
+
}
|
|
8716
|
+
}
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8719
|
+
|
|
8720
|
+
// Step 2: Add query parameters from current pattern
|
|
8721
|
+
if (parsedPattern.queryParams) {
|
|
8722
|
+
|
|
8723
|
+
for (const queryParam of parsedPattern.queryParams) {
|
|
8724
|
+
const paramName = queryParam.name;
|
|
8725
|
+
if (params[paramName] !== undefined && !processedParams.has(paramName)) {
|
|
8726
|
+
queryParams[paramName] = params[paramName];
|
|
8727
|
+
processedParams.add(paramName);
|
|
8728
|
+
}
|
|
8729
|
+
}
|
|
8730
|
+
}
|
|
8731
|
+
|
|
8732
|
+
// Step 3: Add remaining parameters (extra params) alphabetically
|
|
8733
|
+
const extraParams = [];
|
|
8608
8734
|
|
|
8609
|
-
//
|
|
8610
|
-
const
|
|
8611
|
-
(s) => s.type === "
|
|
8735
|
+
// Get all path parameter names to exclude them
|
|
8736
|
+
const pathParamNames = new Set(
|
|
8737
|
+
parsedPattern.segments.filter((s) => s.type === "param").map((s) => s.name),
|
|
8612
8738
|
);
|
|
8613
8739
|
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8740
|
+
for (const [key, value] of Object.entries(params)) {
|
|
8741
|
+
if (
|
|
8742
|
+
!pathParamNames.has(key) &&
|
|
8743
|
+
!processedParams.has(key) &&
|
|
8744
|
+
value !== undefined
|
|
8745
|
+
) {
|
|
8746
|
+
extraParams.push([key, value]);
|
|
8747
|
+
}
|
|
8748
|
+
}
|
|
8623
8749
|
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
}
|
|
8750
|
+
// Sort extra params alphabetically for consistent order
|
|
8751
|
+
extraParams.sort(([a], [b]) => a.localeCompare(b));
|
|
8752
|
+
|
|
8753
|
+
// Add sorted extra params
|
|
8754
|
+
for (const [key, value] of extraParams) {
|
|
8755
|
+
queryParams[key] = value;
|
|
8756
|
+
}
|
|
8757
|
+
|
|
8758
|
+
return queryParams;
|
|
8631
8759
|
};
|
|
8632
8760
|
|
|
8633
8761
|
/**
|
|
8634
8762
|
* Build a URL from a pattern and parameters
|
|
8635
8763
|
*/
|
|
8636
|
-
const buildUrlFromPattern = (
|
|
8764
|
+
const buildUrlFromPattern = (
|
|
8765
|
+
parsedPattern,
|
|
8766
|
+
params = {},
|
|
8767
|
+
originalPattern = null,
|
|
8768
|
+
) => {
|
|
8637
8769
|
if (parsedPattern.segments.length === 0) {
|
|
8638
8770
|
// Root route
|
|
8639
|
-
const
|
|
8771
|
+
const queryParams = {};
|
|
8640
8772
|
for (const [key, value] of Object.entries(params)) {
|
|
8641
8773
|
if (value !== undefined) {
|
|
8642
|
-
|
|
8774
|
+
queryParams[key] = value;
|
|
8643
8775
|
}
|
|
8644
8776
|
}
|
|
8645
|
-
const search =
|
|
8777
|
+
const search = buildQueryString(queryParams);
|
|
8646
8778
|
return `/${search ? `?${search}` : ""}`;
|
|
8647
8779
|
}
|
|
8648
8780
|
|
|
@@ -8656,7 +8788,7 @@ const buildUrlFromPattern = (parsedPattern, params = {}) => {
|
|
|
8656
8788
|
|
|
8657
8789
|
// If value is provided, include it
|
|
8658
8790
|
if (value !== undefined) {
|
|
8659
|
-
segments.push(
|
|
8791
|
+
segments.push(encodeParamValue(value, false)); // Named parameters encode slashes
|
|
8660
8792
|
} else if (!patternSeg.optional) {
|
|
8661
8793
|
// For required parameters without values, keep the placeholder
|
|
8662
8794
|
segments.push(`:${patternSeg.name}`);
|
|
@@ -8710,84 +8842,14 @@ const buildUrlFromPattern = (parsedPattern, params = {}) => {
|
|
|
8710
8842
|
path = path.slice(0, -1);
|
|
8711
8843
|
}
|
|
8712
8844
|
|
|
8713
|
-
//
|
|
8714
|
-
const
|
|
8715
|
-
parsedPattern
|
|
8845
|
+
// Build query parameters respecting hierarchical order
|
|
8846
|
+
const queryParams = buildHierarchicalQueryParams(
|
|
8847
|
+
parsedPattern,
|
|
8848
|
+
params,
|
|
8849
|
+
originalPattern,
|
|
8716
8850
|
);
|
|
8717
8851
|
|
|
8718
|
-
|
|
8719
|
-
const queryParamNames = new Set();
|
|
8720
|
-
const searchParams = new URLSearchParams();
|
|
8721
|
-
|
|
8722
|
-
// Handle pattern-defined query parameters (from ?tab, &lon, etc.)
|
|
8723
|
-
if (parsedPattern.queryParams) {
|
|
8724
|
-
for (const queryParam of parsedPattern.queryParams) {
|
|
8725
|
-
const paramName = queryParam.name;
|
|
8726
|
-
queryParamNames.add(paramName);
|
|
8727
|
-
|
|
8728
|
-
const value = params[paramName];
|
|
8729
|
-
if (value !== undefined) {
|
|
8730
|
-
searchParams.set(paramName, value);
|
|
8731
|
-
}
|
|
8732
|
-
// If no value provided, don't add the parameter to keep URLs clean
|
|
8733
|
-
}
|
|
8734
|
-
}
|
|
8735
|
-
|
|
8736
|
-
// Add remaining parameters as additional query parameters (excluding path and pattern query params)
|
|
8737
|
-
// Handle parameter inheritance and extra parameters
|
|
8738
|
-
const extraParams = [];
|
|
8739
|
-
|
|
8740
|
-
for (const [key, value] of Object.entries(params)) {
|
|
8741
|
-
if (
|
|
8742
|
-
!pathParamNames.has(key) &&
|
|
8743
|
-
!queryParamNames.has(key) &&
|
|
8744
|
-
value !== undefined
|
|
8745
|
-
) {
|
|
8746
|
-
// This parameter doesn't match any path or query parameter in this route pattern,
|
|
8747
|
-
// so it will be treated as an extra query parameter.
|
|
8748
|
-
//
|
|
8749
|
-
// COMMON SCENARIOS:
|
|
8750
|
-
// 1. Parent route parameter inheritance: When a child route has literal segments
|
|
8751
|
-
// that correspond to parent route parameters. For example:
|
|
8752
|
-
// - Parent: /admin/:section/
|
|
8753
|
-
// - Child: /admin/settings/:tab (has "settings" as literal)
|
|
8754
|
-
// - Calling child.buildUrl({section: "toto"}) → /admin/settings?section=toto
|
|
8755
|
-
// The "section" param becomes a query param because "settings" is hardcoded.
|
|
8756
|
-
//
|
|
8757
|
-
// 2. Extra state parameters: Completely additional parameters for URL state
|
|
8758
|
-
// - Calling route.buildUrl({filter: "active"}) → /route?filter=active
|
|
8759
|
-
|
|
8760
|
-
// Check if this parameter value is redundant with literal segments in the path
|
|
8761
|
-
// E.g., don't add "section=settings" if path is already "/admin/settings"
|
|
8762
|
-
const isRedundantWithPath = parsedPattern.segments.some(
|
|
8763
|
-
(segment) => segment.type === "literal" && segment.value === value,
|
|
8764
|
-
);
|
|
8765
|
-
|
|
8766
|
-
if (!isRedundantWithPath) {
|
|
8767
|
-
extraParams.push([key, value]);
|
|
8768
|
-
|
|
8769
|
-
// Optional: Detect and log parent parameter inheritance for debugging
|
|
8770
|
-
detectParentParameterInheritance(
|
|
8771
|
-
key,
|
|
8772
|
-
value,
|
|
8773
|
-
parsedPattern,
|
|
8774
|
-
pathParamNames,
|
|
8775
|
-
queryParamNames,
|
|
8776
|
-
);
|
|
8777
|
-
}
|
|
8778
|
-
// Note: Redundant parameters are intentionally omitted for cleaner URLs
|
|
8779
|
-
}
|
|
8780
|
-
}
|
|
8781
|
-
|
|
8782
|
-
// Sort extra params alphabetically for consistent order
|
|
8783
|
-
extraParams.sort(([a], [b]) => a.localeCompare(b));
|
|
8784
|
-
|
|
8785
|
-
// Add sorted extra params to searchParams
|
|
8786
|
-
for (const [key, value] of extraParams) {
|
|
8787
|
-
searchParams.set(key, value);
|
|
8788
|
-
}
|
|
8789
|
-
|
|
8790
|
-
const search = searchParams.toString();
|
|
8852
|
+
const search = buildQueryString(queryParams);
|
|
8791
8853
|
|
|
8792
8854
|
// No longer handle trailing slash inheritance here
|
|
8793
8855
|
|
|
@@ -18714,14 +18776,6 @@ const RouteLink = ({
|
|
|
18714
18776
|
});
|
|
18715
18777
|
};
|
|
18716
18778
|
|
|
18717
|
-
const rawUrlPartSymbol = Symbol("raw_url_part");
|
|
18718
|
-
const rawUrlPart = (value) => {
|
|
18719
|
-
return {
|
|
18720
|
-
[rawUrlPartSymbol]: true,
|
|
18721
|
-
value,
|
|
18722
|
-
};
|
|
18723
|
-
};
|
|
18724
|
-
|
|
18725
18779
|
installImportMetaCss(import.meta);Object.assign(PSEUDO_CLASSES, {
|
|
18726
18780
|
":-navi-tab-selected": {
|
|
18727
18781
|
attribute: "data-tab-selected"
|