@jsenv/navi 0.16.7 → 0.16.9
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 +187 -135
- 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 = {}) => {
|
|
@@ -7835,26 +7891,24 @@ const createRoutePattern = (pattern) => {
|
|
|
7835
7891
|
};
|
|
7836
7892
|
}
|
|
7837
7893
|
|
|
7838
|
-
// Check for
|
|
7839
|
-
if (
|
|
7894
|
+
// Check for generic parameter-literal conflicts
|
|
7895
|
+
if (!matchesChildLiteral) {
|
|
7840
7896
|
// Check if this is a path parameter from parent pattern
|
|
7841
7897
|
const isParentPathParam = connections.some(
|
|
7842
7898
|
(conn) => conn.paramName === paramName,
|
|
7843
7899
|
);
|
|
7844
7900
|
|
|
7845
7901
|
if (isParentPathParam) {
|
|
7846
|
-
//
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7902
|
+
// Parameter value (from user or signal) doesn't match this child's literals
|
|
7903
|
+
// Check if child has any literal segments that would conflict with this parameter
|
|
7904
|
+
const hasConflictingLiteral = childParsedPattern.segments.some(
|
|
7905
|
+
(segment) =>
|
|
7906
|
+
segment.type === "literal" && segment.value !== paramValue,
|
|
7907
|
+
);
|
|
7850
7908
|
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
(segment) => segment.type === "literal" && segment.value === "settings",
|
|
7855
|
-
);
|
|
7856
|
-
if (hasSettingsLiteral) {
|
|
7857
|
-
return { isCompatible: false };
|
|
7909
|
+
if (hasConflictingLiteral) {
|
|
7910
|
+
return { isCompatible: false };
|
|
7911
|
+
}
|
|
7858
7912
|
}
|
|
7859
7913
|
}
|
|
7860
7914
|
|
|
@@ -8001,7 +8055,9 @@ const createRoutePattern = (pattern) => {
|
|
|
8001
8055
|
const finalParams = removeDefaultValues(resolvedParams);
|
|
8002
8056
|
const currentUrl = buildUrlFromPattern(
|
|
8003
8057
|
parsedPattern,
|
|
8004
|
-
finalParams
|
|
8058
|
+
finalParams,
|
|
8059
|
+
pattern,
|
|
8060
|
+
);
|
|
8005
8061
|
if (currentUrl.length < childUrl.length) {
|
|
8006
8062
|
return currentUrl;
|
|
8007
8063
|
}
|
|
@@ -8105,7 +8161,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8105
8161
|
filteredPattern.trailingSlash = false;
|
|
8106
8162
|
}
|
|
8107
8163
|
|
|
8108
|
-
return buildUrlFromPattern(filteredPattern, finalParams);
|
|
8164
|
+
return buildUrlFromPattern(filteredPattern, finalParams, pattern);
|
|
8109
8165
|
};
|
|
8110
8166
|
|
|
8111
8167
|
/**
|
|
@@ -8160,6 +8216,40 @@ const createRoutePattern = (pattern) => {
|
|
|
8160
8216
|
);
|
|
8161
8217
|
|
|
8162
8218
|
if (optimizedParentUrl) {
|
|
8219
|
+
// Before returning optimized parent URL, check if we need to inherit parameters
|
|
8220
|
+
// from our ancestors that the parent route might not inherit on its own
|
|
8221
|
+
const parentFinalParams = { ...resolvedParams };
|
|
8222
|
+
|
|
8223
|
+
// Remove params that belong to current route (they're at defaults anyway)
|
|
8224
|
+
for (const conn of connections) {
|
|
8225
|
+
delete parentFinalParams[conn.paramName];
|
|
8226
|
+
}
|
|
8227
|
+
|
|
8228
|
+
// Inherit from all ancestor routes, not just immediate parent
|
|
8229
|
+
inheritParentParameters(parentFinalParams, relationships);
|
|
8230
|
+
|
|
8231
|
+
// If we inherited any parameters, add them to the parent URL
|
|
8232
|
+
const extraParamEntries = Object.entries(parentFinalParams).filter(
|
|
8233
|
+
([key, value]) => {
|
|
8234
|
+
// Only include params not handled by parent route
|
|
8235
|
+
const isParentParam = parentPatternObj.connections.some(
|
|
8236
|
+
(conn) => conn.paramName === key,
|
|
8237
|
+
);
|
|
8238
|
+
return !isParentParam && value !== undefined;
|
|
8239
|
+
},
|
|
8240
|
+
);
|
|
8241
|
+
|
|
8242
|
+
if (extraParamEntries.length > 0) {
|
|
8243
|
+
const queryString = buildQueryString(
|
|
8244
|
+
Object.fromEntries(extraParamEntries),
|
|
8245
|
+
);
|
|
8246
|
+
return (
|
|
8247
|
+
optimizedParentUrl +
|
|
8248
|
+
(optimizedParentUrl.includes("?") ? "&" : "?") +
|
|
8249
|
+
queryString
|
|
8250
|
+
);
|
|
8251
|
+
}
|
|
8252
|
+
|
|
8163
8253
|
return optimizedParentUrl;
|
|
8164
8254
|
}
|
|
8165
8255
|
}
|
|
@@ -8591,58 +8681,98 @@ const extractSearchParams = (urlObj, connections = []) => {
|
|
|
8591
8681
|
};
|
|
8592
8682
|
|
|
8593
8683
|
/**
|
|
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
|
|
8684
|
+
* Build query parameters respecting hierarchical order from ancestor patterns
|
|
8597
8685
|
*/
|
|
8598
|
-
const
|
|
8599
|
-
paramName,
|
|
8600
|
-
paramValue,
|
|
8686
|
+
const buildHierarchicalQueryParams = (
|
|
8601
8687
|
parsedPattern,
|
|
8602
|
-
|
|
8603
|
-
|
|
8688
|
+
params,
|
|
8689
|
+
originalPattern,
|
|
8604
8690
|
) => {
|
|
8605
|
-
|
|
8606
|
-
const
|
|
8607
|
-
|
|
8691
|
+
const queryParams = {};
|
|
8692
|
+
const processedParams = new Set();
|
|
8693
|
+
|
|
8694
|
+
// Get relationships for this pattern
|
|
8695
|
+
const relationships = patternRelationships.get(originalPattern);
|
|
8696
|
+
const parentPatterns = relationships?.parentPatterns || [];
|
|
8697
|
+
|
|
8698
|
+
// Step 1: Add query parameters from ancestor patterns (oldest to newest)
|
|
8699
|
+
// This ensures ancestor parameters come first in their declaration order
|
|
8700
|
+
const ancestorPatterns = parentPatterns; // Process in order: root ancestor first, then immediate parent
|
|
8701
|
+
|
|
8702
|
+
for (const ancestorPatternObj of ancestorPatterns) {
|
|
8703
|
+
if (ancestorPatternObj.pattern?.queryParams) {
|
|
8704
|
+
|
|
8705
|
+
for (const queryParam of ancestorPatternObj.pattern.queryParams) {
|
|
8706
|
+
const paramName = queryParam.name;
|
|
8707
|
+
if (
|
|
8708
|
+
params[paramName] !== undefined &&
|
|
8709
|
+
!processedParams.has(paramName)
|
|
8710
|
+
) {
|
|
8711
|
+
queryParams[paramName] = params[paramName];
|
|
8712
|
+
processedParams.add(paramName);
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
}
|
|
8716
|
+
}
|
|
8717
|
+
|
|
8718
|
+
// Step 2: Add query parameters from current pattern
|
|
8719
|
+
if (parsedPattern.queryParams) {
|
|
8720
|
+
|
|
8721
|
+
for (const queryParam of parsedPattern.queryParams) {
|
|
8722
|
+
const paramName = queryParam.name;
|
|
8723
|
+
if (params[paramName] !== undefined && !processedParams.has(paramName)) {
|
|
8724
|
+
queryParams[paramName] = params[paramName];
|
|
8725
|
+
processedParams.add(paramName);
|
|
8726
|
+
}
|
|
8727
|
+
}
|
|
8728
|
+
}
|
|
8608
8729
|
|
|
8609
|
-
//
|
|
8610
|
-
const
|
|
8611
|
-
|
|
8730
|
+
// Step 3: Add remaining parameters (extra params) alphabetically
|
|
8731
|
+
const extraParams = [];
|
|
8732
|
+
|
|
8733
|
+
// Get all path parameter names to exclude them
|
|
8734
|
+
const pathParamNames = new Set(
|
|
8735
|
+
parsedPattern.segments.filter((s) => s.type === "param").map((s) => s.name),
|
|
8612
8736
|
);
|
|
8613
8737
|
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8738
|
+
for (const [key, value] of Object.entries(params)) {
|
|
8739
|
+
if (
|
|
8740
|
+
!pathParamNames.has(key) &&
|
|
8741
|
+
!processedParams.has(key) &&
|
|
8742
|
+
value !== undefined
|
|
8743
|
+
) {
|
|
8744
|
+
extraParams.push([key, value]);
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8623
8747
|
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
}
|
|
8748
|
+
// Sort extra params alphabetically for consistent order
|
|
8749
|
+
extraParams.sort(([a], [b]) => a.localeCompare(b));
|
|
8750
|
+
|
|
8751
|
+
// Add sorted extra params
|
|
8752
|
+
for (const [key, value] of extraParams) {
|
|
8753
|
+
queryParams[key] = value;
|
|
8754
|
+
}
|
|
8755
|
+
|
|
8756
|
+
return queryParams;
|
|
8631
8757
|
};
|
|
8632
8758
|
|
|
8633
8759
|
/**
|
|
8634
8760
|
* Build a URL from a pattern and parameters
|
|
8635
8761
|
*/
|
|
8636
|
-
const buildUrlFromPattern = (
|
|
8762
|
+
const buildUrlFromPattern = (
|
|
8763
|
+
parsedPattern,
|
|
8764
|
+
params = {},
|
|
8765
|
+
originalPattern = null,
|
|
8766
|
+
) => {
|
|
8637
8767
|
if (parsedPattern.segments.length === 0) {
|
|
8638
8768
|
// Root route
|
|
8639
|
-
const
|
|
8769
|
+
const queryParams = {};
|
|
8640
8770
|
for (const [key, value] of Object.entries(params)) {
|
|
8641
8771
|
if (value !== undefined) {
|
|
8642
|
-
|
|
8772
|
+
queryParams[key] = value;
|
|
8643
8773
|
}
|
|
8644
8774
|
}
|
|
8645
|
-
const search =
|
|
8775
|
+
const search = buildQueryString(queryParams);
|
|
8646
8776
|
return `/${search ? `?${search}` : ""}`;
|
|
8647
8777
|
}
|
|
8648
8778
|
|
|
@@ -8656,7 +8786,7 @@ const buildUrlFromPattern = (parsedPattern, params = {}) => {
|
|
|
8656
8786
|
|
|
8657
8787
|
// If value is provided, include it
|
|
8658
8788
|
if (value !== undefined) {
|
|
8659
|
-
segments.push(
|
|
8789
|
+
segments.push(encodeParamValue(value, false)); // Named parameters encode slashes
|
|
8660
8790
|
} else if (!patternSeg.optional) {
|
|
8661
8791
|
// For required parameters without values, keep the placeholder
|
|
8662
8792
|
segments.push(`:${patternSeg.name}`);
|
|
@@ -8710,84 +8840,14 @@ const buildUrlFromPattern = (parsedPattern, params = {}) => {
|
|
|
8710
8840
|
path = path.slice(0, -1);
|
|
8711
8841
|
}
|
|
8712
8842
|
|
|
8713
|
-
//
|
|
8714
|
-
const
|
|
8715
|
-
parsedPattern
|
|
8843
|
+
// Build query parameters respecting hierarchical order
|
|
8844
|
+
const queryParams = buildHierarchicalQueryParams(
|
|
8845
|
+
parsedPattern,
|
|
8846
|
+
params,
|
|
8847
|
+
originalPattern,
|
|
8716
8848
|
);
|
|
8717
8849
|
|
|
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();
|
|
8850
|
+
const search = buildQueryString(queryParams);
|
|
8791
8851
|
|
|
8792
8852
|
// No longer handle trailing slash inheritance here
|
|
8793
8853
|
|
|
@@ -18714,14 +18774,6 @@ const RouteLink = ({
|
|
|
18714
18774
|
});
|
|
18715
18775
|
};
|
|
18716
18776
|
|
|
18717
|
-
const rawUrlPartSymbol = Symbol("raw_url_part");
|
|
18718
|
-
const rawUrlPart = (value) => {
|
|
18719
|
-
return {
|
|
18720
|
-
[rawUrlPartSymbol]: true,
|
|
18721
|
-
value,
|
|
18722
|
-
};
|
|
18723
|
-
};
|
|
18724
|
-
|
|
18725
18777
|
installImportMetaCss(import.meta);Object.assign(PSEUDO_CLASSES, {
|
|
18726
18778
|
":-navi-tab-selected": {
|
|
18727
18779
|
attribute: "data-tab-selected"
|