@jsenv/navi 0.16.1 → 0.16.2
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 +130 -23
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -7522,21 +7522,31 @@ setBaseUrl(
|
|
|
7522
7522
|
// Pattern registry for building relationships before routes are created
|
|
7523
7523
|
const patternRegistry = new Map(); // pattern -> patternData
|
|
7524
7524
|
const patternRelationships = new Map(); // pattern -> relationships
|
|
7525
|
+
let patternsRegistered = false;
|
|
7525
7526
|
|
|
7526
7527
|
// Function to detect signals in route patterns and connect them
|
|
7527
7528
|
const detectSignals = (routePattern) => {
|
|
7528
7529
|
const signalConnections = [];
|
|
7529
7530
|
let updatedPattern = routePattern;
|
|
7530
7531
|
|
|
7531
|
-
// Look for signals in
|
|
7532
|
-
//
|
|
7533
|
-
|
|
7532
|
+
// Look for signals in two formats:
|
|
7533
|
+
// 1. Expected format: :paramName={navi_state_signal:id} or ?paramName={navi_state_signal:id} or ¶mName={navi_state_signal:id}
|
|
7534
|
+
// 2. Typoe format (missing = sign): ¶mName{navi_state_signal:id}
|
|
7535
|
+
const signalParamRegex = /([?:&])(\w+)(=)?(\{navi_state_signal:[^}]+\})/g;
|
|
7534
7536
|
let match;
|
|
7535
7537
|
|
|
7536
7538
|
while ((match = signalParamRegex.exec(routePattern)) !== null) {
|
|
7537
|
-
const [fullMatch, prefix, paramName, signalString] = match;
|
|
7539
|
+
const [fullMatch, prefix, paramName, equalSign, signalString] = match;
|
|
7538
7540
|
|
|
7539
|
-
//
|
|
7541
|
+
// Emit warning if equal sign is missing
|
|
7542
|
+
if (!equalSign) {
|
|
7543
|
+
console.warn(
|
|
7544
|
+
`[detectSignals] Missing '=' sign in route pattern: "${prefix}${paramName}${signalString}". ` +
|
|
7545
|
+
`Consider using "${prefix}${paramName}=${signalString}" for better clarity.`,
|
|
7546
|
+
);
|
|
7547
|
+
}
|
|
7548
|
+
|
|
7549
|
+
// Extract the signal ID from the format: {navi_state_signal:id}
|
|
7540
7550
|
const signalIdMatch = signalString.match(/\{navi_state_signal:([^}]+)\}/);
|
|
7541
7551
|
if (!signalIdMatch) {
|
|
7542
7552
|
console.warn(
|
|
@@ -7553,13 +7563,10 @@ const detectSignals = (routePattern) => {
|
|
|
7553
7563
|
|
|
7554
7564
|
let replacement;
|
|
7555
7565
|
if (prefix === ":") {
|
|
7556
|
-
// Path parameter: :section=
|
|
7566
|
+
// Path parameter: :section={navi_state_signal:...} becomes :section
|
|
7557
7567
|
replacement = `${prefix}${paramName}`;
|
|
7558
|
-
} else if (prefix === "?") {
|
|
7559
|
-
//
|
|
7560
|
-
replacement = `${prefix}${paramName}`;
|
|
7561
|
-
} else if (prefix === "&") {
|
|
7562
|
-
// Additional search parameter: &lon=__jsenv_signal_1__ becomes &lon
|
|
7568
|
+
} else if (prefix === "?" || prefix === "&") {
|
|
7569
|
+
// Query parameter: ?city={navi_state_signal:...} or &lon{navi_state_signal:...} becomes ?city or &lon
|
|
7563
7570
|
replacement = `${prefix}${paramName}`;
|
|
7564
7571
|
}
|
|
7565
7572
|
updatedPattern = updatedPattern.replace(fullMatch, replacement);
|
|
@@ -7606,6 +7613,7 @@ const createRoutePattern = (pattern) => {
|
|
|
7606
7613
|
const result = matchUrl(parsedPattern, url, {
|
|
7607
7614
|
parameterDefaults,
|
|
7608
7615
|
baseUrl,
|
|
7616
|
+
connections,
|
|
7609
7617
|
});
|
|
7610
7618
|
|
|
7611
7619
|
return result;
|
|
@@ -7985,7 +7993,11 @@ const checkIfLiteralCanBeOptional = (literalValue, patternRegistry) => {
|
|
|
7985
7993
|
/**
|
|
7986
7994
|
* Match a URL against a parsed pattern
|
|
7987
7995
|
*/
|
|
7988
|
-
const matchUrl = (
|
|
7996
|
+
const matchUrl = (
|
|
7997
|
+
parsedPattern,
|
|
7998
|
+
url,
|
|
7999
|
+
{ parameterDefaults, baseUrl, connections = [] },
|
|
8000
|
+
) => {
|
|
7989
8001
|
// Parse the URL
|
|
7990
8002
|
const urlObj = new URL(url, baseUrl);
|
|
7991
8003
|
let pathname = urlObj.pathname;
|
|
@@ -8007,14 +8019,14 @@ const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
|
|
|
8007
8019
|
// OR when URL exactly matches baseUrl (treating baseUrl as root)
|
|
8008
8020
|
if (parsedPattern.segments.length === 0) {
|
|
8009
8021
|
if (pathname === "/" || pathname === "") {
|
|
8010
|
-
return extractSearchParams(urlObj);
|
|
8022
|
+
return extractSearchParams(urlObj, connections);
|
|
8011
8023
|
}
|
|
8012
8024
|
|
|
8013
8025
|
// Special case: if URL exactly matches baseUrl, treat as root route
|
|
8014
8026
|
if (baseUrl) {
|
|
8015
8027
|
const baseUrlObj = new URL(baseUrl);
|
|
8016
8028
|
if (originalPathname === baseUrlObj.pathname) {
|
|
8017
|
-
return extractSearchParams(urlObj);
|
|
8029
|
+
return extractSearchParams(urlObj, connections);
|
|
8018
8030
|
}
|
|
8019
8031
|
}
|
|
8020
8032
|
|
|
@@ -8109,7 +8121,7 @@ const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
|
|
|
8109
8121
|
// If pattern has trailing slash or wildcard, allow extra segments (no additional check needed)
|
|
8110
8122
|
|
|
8111
8123
|
// Add search parameters
|
|
8112
|
-
const searchParams = extractSearchParams(urlObj);
|
|
8124
|
+
const searchParams = extractSearchParams(urlObj, connections);
|
|
8113
8125
|
Object.assign(params, searchParams);
|
|
8114
8126
|
|
|
8115
8127
|
// Apply remaining parameter defaults for unmatched parameters
|
|
@@ -8125,10 +8137,29 @@ const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
|
|
|
8125
8137
|
/**
|
|
8126
8138
|
* Extract search parameters from URL
|
|
8127
8139
|
*/
|
|
8128
|
-
const extractSearchParams = (urlObj) => {
|
|
8140
|
+
const extractSearchParams = (urlObj, connections = []) => {
|
|
8129
8141
|
const params = {};
|
|
8142
|
+
|
|
8143
|
+
// Create a map for quick signal type lookup
|
|
8144
|
+
const signalTypes = new Map();
|
|
8145
|
+
for (const connection of connections) {
|
|
8146
|
+
if (connection.options.type) {
|
|
8147
|
+
signalTypes.set(connection.paramName, connection.options.type);
|
|
8148
|
+
}
|
|
8149
|
+
}
|
|
8150
|
+
|
|
8130
8151
|
for (const [key, value] of urlObj.searchParams) {
|
|
8131
|
-
|
|
8152
|
+
const signalType = signalTypes.get(key);
|
|
8153
|
+
|
|
8154
|
+
// Cast value based on signal type
|
|
8155
|
+
if (signalType === "number" || signalType === "float") {
|
|
8156
|
+
const numberValue = Number(value);
|
|
8157
|
+
params[key] = isNaN(numberValue) ? value : numberValue;
|
|
8158
|
+
} else if (signalType === "boolean") {
|
|
8159
|
+
params[key] = value === "true" || value === "1";
|
|
8160
|
+
} else {
|
|
8161
|
+
params[key] = value;
|
|
8162
|
+
}
|
|
8132
8163
|
}
|
|
8133
8164
|
return params;
|
|
8134
8165
|
};
|
|
@@ -8402,6 +8433,8 @@ const setupPatterns = (patternDefinitions) => {
|
|
|
8402
8433
|
originalPattern: currentPattern,
|
|
8403
8434
|
});
|
|
8404
8435
|
}
|
|
8436
|
+
|
|
8437
|
+
patternsRegistered = true;
|
|
8405
8438
|
};
|
|
8406
8439
|
|
|
8407
8440
|
/**
|
|
@@ -8411,12 +8444,25 @@ const getPatternData = (urlPatternRaw) => {
|
|
|
8411
8444
|
return patternRegistry.get(urlPatternRaw);
|
|
8412
8445
|
};
|
|
8413
8446
|
|
|
8447
|
+
/**
|
|
8448
|
+
* Get pattern relationships for route creation
|
|
8449
|
+
*/
|
|
8450
|
+
const getPatternRelationships = () => {
|
|
8451
|
+
if (!patternsRegistered) {
|
|
8452
|
+
throw new Error(
|
|
8453
|
+
"Patterns must be registered before accessing relationships",
|
|
8454
|
+
);
|
|
8455
|
+
}
|
|
8456
|
+
return patternRelationships;
|
|
8457
|
+
};
|
|
8458
|
+
|
|
8414
8459
|
/**
|
|
8415
8460
|
* Clear all registered patterns
|
|
8416
8461
|
*/
|
|
8417
8462
|
const clearPatterns = () => {
|
|
8418
8463
|
patternRegistry.clear();
|
|
8419
8464
|
patternRelationships.clear();
|
|
8465
|
+
patternsRegistered = false;
|
|
8420
8466
|
};
|
|
8421
8467
|
|
|
8422
8468
|
const resolveRouteUrl = (relativeUrl) => {
|
|
@@ -8633,6 +8679,41 @@ const getRoutePrivateProperties = (route) => {
|
|
|
8633
8679
|
return routePrivatePropertiesMap.get(route);
|
|
8634
8680
|
};
|
|
8635
8681
|
|
|
8682
|
+
/**
|
|
8683
|
+
* Get child routes of a given route
|
|
8684
|
+
*/
|
|
8685
|
+
const getRouteChildren = (route) => {
|
|
8686
|
+
const children = [];
|
|
8687
|
+
const routePrivateProperties = getRoutePrivateProperties(route);
|
|
8688
|
+
if (!routePrivateProperties) {
|
|
8689
|
+
return children;
|
|
8690
|
+
}
|
|
8691
|
+
|
|
8692
|
+
const { originalPattern } = routePrivateProperties;
|
|
8693
|
+
const relationships = getPatternRelationships();
|
|
8694
|
+
const relationshipData = relationships.get(originalPattern);
|
|
8695
|
+
|
|
8696
|
+
if (!relationshipData || !relationshipData.children) {
|
|
8697
|
+
return children;
|
|
8698
|
+
}
|
|
8699
|
+
|
|
8700
|
+
// Find child routes
|
|
8701
|
+
for (const childPattern of relationshipData.children) {
|
|
8702
|
+
for (const otherRoute of routeSet) {
|
|
8703
|
+
const otherRoutePrivateProperties = getRoutePrivateProperties(otherRoute);
|
|
8704
|
+
if (
|
|
8705
|
+
otherRoutePrivateProperties &&
|
|
8706
|
+
otherRoutePrivateProperties.originalPattern === childPattern
|
|
8707
|
+
) {
|
|
8708
|
+
children.push(otherRoute);
|
|
8709
|
+
break;
|
|
8710
|
+
}
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8713
|
+
|
|
8714
|
+
return children;
|
|
8715
|
+
};
|
|
8716
|
+
|
|
8636
8717
|
const registerRoute = (routePattern) => {
|
|
8637
8718
|
const urlPatternRaw = routePattern.originalPattern;
|
|
8638
8719
|
const patternData = getPatternData(urlPatternRaw);
|
|
@@ -8784,13 +8865,39 @@ const registerRoute = (routePattern) => {
|
|
|
8784
8865
|
);
|
|
8785
8866
|
return null;
|
|
8786
8867
|
}
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8868
|
+
|
|
8869
|
+
// Walk down the hierarchy updating action params and tracking most specific route
|
|
8870
|
+
let currentRoute = route;
|
|
8871
|
+
let mostSpecificRoute;
|
|
8872
|
+
|
|
8873
|
+
while (currentRoute) {
|
|
8874
|
+
if (!currentRoute.matching) {
|
|
8875
|
+
break;
|
|
8876
|
+
}
|
|
8877
|
+
|
|
8878
|
+
// Update the most specific route as we go
|
|
8879
|
+
mostSpecificRoute = currentRoute;
|
|
8880
|
+
// Update action params
|
|
8881
|
+
if (currentRoute.action) {
|
|
8882
|
+
const currentRoutePrivateProperties =
|
|
8883
|
+
getRoutePrivateProperties(currentRoute);
|
|
8884
|
+
if (currentRoutePrivateProperties) {
|
|
8885
|
+
const { routePattern: currentRoutePattern } =
|
|
8886
|
+
currentRoutePrivateProperties;
|
|
8887
|
+
const currentResolvedParams = currentRoutePattern.resolveParams();
|
|
8888
|
+
const updatedActionParams = {
|
|
8889
|
+
...currentResolvedParams,
|
|
8890
|
+
...newParams,
|
|
8891
|
+
};
|
|
8892
|
+
currentRoute.action.replaceParams(updatedActionParams);
|
|
8893
|
+
}
|
|
8894
|
+
}
|
|
8895
|
+
|
|
8896
|
+
// Find the first matching child to continue down the hierarchy
|
|
8897
|
+
const children = getRouteChildren(currentRoute);
|
|
8898
|
+
currentRoute = children.find((child) => child.matching) || null;
|
|
8792
8899
|
}
|
|
8793
|
-
return
|
|
8900
|
+
return mostSpecificRoute.redirectTo(newParams);
|
|
8794
8901
|
};
|
|
8795
8902
|
route.buildRelativeUrl = (params) => {
|
|
8796
8903
|
// buildMostPreciseUrl now handles parameter resolution internally
|