@jsenv/navi 0.16.29 → 0.16.31
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 +250 -292
- package/dist/jsenv_navi.js.map +5 -5
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -2499,7 +2499,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2499
2499
|
if (globalSignalRegistry.has(signalIdString)) {
|
|
2500
2500
|
const conflictInfo = globalSignalRegistry.get(signalIdString);
|
|
2501
2501
|
throw new Error(
|
|
2502
|
-
`Signal ID conflict: A signal with ID "${signalIdString}" already exists (existing default: ${conflictInfo.options.
|
|
2502
|
+
`Signal ID conflict: A signal with ID "${signalIdString}" already exists (existing default: ${conflictInfo.options.getDefaultValue()})`,
|
|
2503
2503
|
);
|
|
2504
2504
|
}
|
|
2505
2505
|
|
|
@@ -2509,18 +2509,13 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2509
2509
|
persists
|
|
2510
2510
|
? valueInLocalStorage(localStorageKey, { type })
|
|
2511
2511
|
: NO_LOCAL_STORAGE;
|
|
2512
|
+
/**
|
|
2513
|
+
* Returns the current default value from code logic only (static or dynamic).
|
|
2514
|
+
* NEVER considers localStorage - used for URL building and route matching.
|
|
2515
|
+
*
|
|
2516
|
+
* @returns {any} The current code default value, undefined if no default
|
|
2517
|
+
*/
|
|
2512
2518
|
const getDefaultValue = () => {
|
|
2513
|
-
if (persists) {
|
|
2514
|
-
const valueFromLocalStorage = readFromLocalStorage();
|
|
2515
|
-
if (valueFromLocalStorage !== undefined) {
|
|
2516
|
-
if (debug) {
|
|
2517
|
-
console.debug(
|
|
2518
|
-
`[stateSignal:${signalIdString}] using value from localStorage "${localStorageKey}"=${valueFromLocalStorage}`,
|
|
2519
|
-
);
|
|
2520
|
-
}
|
|
2521
|
-
return valueFromLocalStorage;
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
2519
|
if (dynamicDefaultSignal) {
|
|
2525
2520
|
const dynamicValue = dynamicDefaultSignal.peek();
|
|
2526
2521
|
if (dynamicValue === undefined) {
|
|
@@ -2548,6 +2543,27 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2548
2543
|
}
|
|
2549
2544
|
return staticDefaultValue;
|
|
2550
2545
|
};
|
|
2546
|
+
|
|
2547
|
+
/**
|
|
2548
|
+
* Returns fallback value: localStorage first, then code default.
|
|
2549
|
+
* Used for signal initialization and resets.
|
|
2550
|
+
*
|
|
2551
|
+
* @returns {any} The fallback value (localStorage or code default)
|
|
2552
|
+
*/
|
|
2553
|
+
const getFallbackValue = () => {
|
|
2554
|
+
if (persists) {
|
|
2555
|
+
const valueFromLocalStorage = readFromLocalStorage();
|
|
2556
|
+
if (valueFromLocalStorage !== undefined) {
|
|
2557
|
+
if (debug) {
|
|
2558
|
+
console.debug(
|
|
2559
|
+
`[stateSignal:${signalIdString}] using value from localStorage "${localStorageKey}"=${valueFromLocalStorage}`,
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
return valueFromLocalStorage;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
return getDefaultValue();
|
|
2566
|
+
};
|
|
2551
2567
|
const isCustomValue = (value) => {
|
|
2552
2568
|
if (value === undefined) {
|
|
2553
2569
|
return false;
|
|
@@ -2563,7 +2579,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2563
2579
|
};
|
|
2564
2580
|
|
|
2565
2581
|
// Create signal with initial value: use stored value, or undefined to indicate no explicit value
|
|
2566
|
-
const advancedSignal = signal(
|
|
2582
|
+
const advancedSignal = signal(getFallbackValue());
|
|
2567
2583
|
const validity = { valid: true };
|
|
2568
2584
|
advancedSignal.validity = validity;
|
|
2569
2585
|
advancedSignal.__signalId = signalIdString;
|
|
@@ -2581,7 +2597,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2581
2597
|
if (value !== undefined) {
|
|
2582
2598
|
return;
|
|
2583
2599
|
}
|
|
2584
|
-
const defaultValue =
|
|
2600
|
+
const defaultValue = getFallbackValue();
|
|
2585
2601
|
if (defaultValue === value) {
|
|
2586
2602
|
return;
|
|
2587
2603
|
}
|
|
@@ -2630,7 +2646,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2630
2646
|
}
|
|
2631
2647
|
|
|
2632
2648
|
// Signal was using default value, update to new default
|
|
2633
|
-
const newDefaultValue =
|
|
2649
|
+
const newDefaultValue = getFallbackValue();
|
|
2634
2650
|
if (newDefaultValue === value) {
|
|
2635
2651
|
dynamicDefaultPreviousValue = dynamicDefaultValue;
|
|
2636
2652
|
return;
|
|
@@ -2711,8 +2727,8 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2711
2727
|
globalSignalRegistry.set(signalIdString, {
|
|
2712
2728
|
signal: advancedSignal,
|
|
2713
2729
|
options: {
|
|
2730
|
+
staticDefaultValue,
|
|
2714
2731
|
getDefaultValue,
|
|
2715
|
-
defaultValue: staticDefaultValue,
|
|
2716
2732
|
dynamicDefaultSignal,
|
|
2717
2733
|
isCustomValue,
|
|
2718
2734
|
type,
|
|
@@ -7755,9 +7771,9 @@ const detectSignals = (routePattern) => {
|
|
|
7755
7771
|
updatedPattern = updatedPattern.replace(fullMatch, replacement);
|
|
7756
7772
|
|
|
7757
7773
|
signalConnections.push({
|
|
7758
|
-
signal,
|
|
7759
7774
|
paramName,
|
|
7760
|
-
|
|
7775
|
+
signal,
|
|
7776
|
+
...options,
|
|
7761
7777
|
});
|
|
7762
7778
|
} else {
|
|
7763
7779
|
console.warn(
|
|
@@ -7780,30 +7796,18 @@ const detectSignals = (routePattern) => {
|
|
|
7780
7796
|
const createRoutePattern = (pattern) => {
|
|
7781
7797
|
// Detect and process signals in the pattern first
|
|
7782
7798
|
const [cleanPattern, connections] = detectSignals(pattern);
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
const parameterDefaults = new Map();
|
|
7786
|
-
for (const connection of connections) {
|
|
7787
|
-
const { paramName, options } = connection;
|
|
7788
|
-
if (options.defaultValue !== undefined) {
|
|
7789
|
-
parameterDefaults.set(paramName, options.defaultValue);
|
|
7790
|
-
}
|
|
7791
|
-
}
|
|
7792
|
-
|
|
7793
|
-
const parsedPattern = parsePattern(
|
|
7794
|
-
cleanPattern,
|
|
7795
|
-
parameterDefaults,
|
|
7796
|
-
connections,
|
|
7797
|
-
);
|
|
7798
|
-
|
|
7799
|
+
// Build parameter connection map for efficient lookups
|
|
7800
|
+
const connectionMap = new Map();
|
|
7799
7801
|
// Create signalSet to track all signals this pattern depends on
|
|
7800
7802
|
const signalSet = new Set();
|
|
7801
7803
|
for (const connection of connections) {
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7804
|
+
connectionMap.set(connection.paramName, connection);
|
|
7805
|
+
|
|
7806
|
+
signalSet.add(connection.signal);
|
|
7805
7807
|
}
|
|
7806
7808
|
|
|
7809
|
+
const parsedPattern = parsePattern(cleanPattern, connectionMap);
|
|
7810
|
+
|
|
7807
7811
|
if (DEBUG$2) {
|
|
7808
7812
|
console.debug(`[CustomPattern] Created pattern:`, parsedPattern);
|
|
7809
7813
|
console.debug(`[CustomPattern] Signal connections:`, connections);
|
|
@@ -7812,7 +7816,6 @@ const createRoutePattern = (pattern) => {
|
|
|
7812
7816
|
|
|
7813
7817
|
const applyOn = (url) => {
|
|
7814
7818
|
const result = matchUrl(parsedPattern, url, {
|
|
7815
|
-
parameterDefaults,
|
|
7816
7819
|
baseUrl,
|
|
7817
7820
|
connections,
|
|
7818
7821
|
patternObj: patternObject,
|
|
@@ -7832,18 +7835,34 @@ const createRoutePattern = (pattern) => {
|
|
|
7832
7835
|
let resolvedParams = { ...providedParams };
|
|
7833
7836
|
|
|
7834
7837
|
// Process all connections for parameter resolution
|
|
7835
|
-
for (const connection of
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7838
|
+
for (const [paramName, connection] of connectionMap) {
|
|
7839
|
+
if (paramName in providedParams) {
|
|
7840
|
+
// Parameter was explicitly provided - always respect explicit parameters
|
|
7841
|
+
// Don't check signal value - explicit parameter takes precedence
|
|
7842
|
+
continue;
|
|
7843
|
+
}
|
|
7844
|
+
const signalValue = connection.signal.value;
|
|
7845
|
+
if (signalValue !== undefined) {
|
|
7839
7846
|
// Parameter was not provided, check signal value
|
|
7840
|
-
resolvedParams[paramName] =
|
|
7847
|
+
resolvedParams[paramName] = signalValue;
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
|
|
7851
|
+
// Add defaults for parameters that are still missing
|
|
7852
|
+
// Use current dynamic defaults from signal connections
|
|
7853
|
+
for (const [paramName, connection] of connectionMap) {
|
|
7854
|
+
if (paramName in resolvedParams) {
|
|
7855
|
+
continue;
|
|
7856
|
+
}
|
|
7857
|
+
const currentDefault = connection.getDefaultValue();
|
|
7858
|
+
if (currentDefault !== undefined) {
|
|
7859
|
+
resolvedParams[paramName] = currentDefault;
|
|
7841
7860
|
}
|
|
7842
7861
|
}
|
|
7843
7862
|
|
|
7844
7863
|
// Include active non-default parameters from child routes for URL optimization
|
|
7845
7864
|
// Only include from child routes that would actually match the current parameters
|
|
7846
|
-
const childPatternObjs = patternObject.children
|
|
7865
|
+
const childPatternObjs = patternObject.children;
|
|
7847
7866
|
for (const childPatternObj of childPatternObjs) {
|
|
7848
7867
|
// Check if this child route would match the current resolved parameters
|
|
7849
7868
|
// by simulating URL building and seeing if the child segments align
|
|
@@ -7869,20 +7888,20 @@ const createRoutePattern = (pattern) => {
|
|
|
7869
7888
|
}
|
|
7870
7889
|
|
|
7871
7890
|
if (childWouldMatch) {
|
|
7872
|
-
for (const
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7891
|
+
for (const [
|
|
7892
|
+
childParam,
|
|
7893
|
+
childConnection,
|
|
7894
|
+
] of childPatternObj.connectionMap) {
|
|
7895
|
+
if (childParam in resolvedParams) {
|
|
7896
|
+
continue;
|
|
7897
|
+
}
|
|
7898
|
+
const childSignalValue = childConnection.signal.value;
|
|
7879
7899
|
// Only include if not already resolved and is non-default
|
|
7880
7900
|
if (
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
childSignal.value !== childOptions.defaultValue
|
|
7901
|
+
childSignalValue !== undefined &&
|
|
7902
|
+
childSignalValue !== childConnection.getDefaultValue()
|
|
7884
7903
|
) {
|
|
7885
|
-
resolvedParams[childParam] =
|
|
7904
|
+
resolvedParams[childParam] = childSignalValue;
|
|
7886
7905
|
}
|
|
7887
7906
|
}
|
|
7888
7907
|
}
|
|
@@ -7907,18 +7926,21 @@ const createRoutePattern = (pattern) => {
|
|
|
7907
7926
|
const removeDefaultValues = (params) => {
|
|
7908
7927
|
const filtered = { ...params };
|
|
7909
7928
|
|
|
7910
|
-
for (const connection of
|
|
7911
|
-
const { paramName, signal, options } = connection;
|
|
7912
|
-
|
|
7929
|
+
for (const [paramName, connection] of connectionMap) {
|
|
7913
7930
|
if (paramName in filtered) {
|
|
7914
7931
|
// Parameter is explicitly provided - check if we should remove it
|
|
7915
|
-
|
|
7916
|
-
|
|
7932
|
+
const paramValue = filtered[paramName];
|
|
7933
|
+
|
|
7934
|
+
if (!connection.isCustomValue(paramValue)) {
|
|
7917
7935
|
delete filtered[paramName];
|
|
7918
7936
|
}
|
|
7919
|
-
} else
|
|
7920
|
-
// Parameter not provided but signal has
|
|
7921
|
-
|
|
7937
|
+
} else {
|
|
7938
|
+
// Parameter not provided but signal has a value
|
|
7939
|
+
const signalValue = connection.signal.value;
|
|
7940
|
+
if (connection.isCustomValue(signalValue)) {
|
|
7941
|
+
// Only include custom values
|
|
7942
|
+
filtered[paramName] = signalValue;
|
|
7943
|
+
}
|
|
7922
7944
|
}
|
|
7923
7945
|
}
|
|
7924
7946
|
|
|
@@ -7931,12 +7953,11 @@ const createRoutePattern = (pattern) => {
|
|
|
7931
7953
|
const canReachLiteralValue = (literalValue, params) => {
|
|
7932
7954
|
// Check parent's own parameters (signals and user params)
|
|
7933
7955
|
const parentCanProvide = connections.some((conn) => {
|
|
7934
|
-
const signalValue = conn.signal
|
|
7956
|
+
const signalValue = conn.signal.value;
|
|
7935
7957
|
const userValue = params[conn.paramName];
|
|
7936
7958
|
const effectiveValue = userValue !== undefined ? userValue : signalValue;
|
|
7937
7959
|
return (
|
|
7938
|
-
effectiveValue === literalValue &&
|
|
7939
|
-
conn.options.isCustomValue?.(effectiveValue)
|
|
7960
|
+
effectiveValue === literalValue && conn.isCustomValue(effectiveValue)
|
|
7940
7961
|
);
|
|
7941
7962
|
});
|
|
7942
7963
|
|
|
@@ -7959,7 +7980,7 @@ const createRoutePattern = (pattern) => {
|
|
|
7959
7980
|
|
|
7960
7981
|
const getDescendantSignals = (pattern) => {
|
|
7961
7982
|
const signals = [...pattern.connections];
|
|
7962
|
-
for (const child of pattern.children
|
|
7983
|
+
for (const child of pattern.children) {
|
|
7963
7984
|
signals.push(...getDescendantSignals(child));
|
|
7964
7985
|
}
|
|
7965
7986
|
return signals;
|
|
@@ -7971,11 +7992,8 @@ const createRoutePattern = (pattern) => {
|
|
|
7971
7992
|
];
|
|
7972
7993
|
|
|
7973
7994
|
const systemCanProvide = allRelevantSignals.some((conn) => {
|
|
7974
|
-
const signalValue = conn.signal
|
|
7975
|
-
return (
|
|
7976
|
-
signalValue === literalValue &&
|
|
7977
|
-
conn.options.isCustomValue?.(signalValue)
|
|
7978
|
-
);
|
|
7995
|
+
const signalValue = conn.signal.value;
|
|
7996
|
+
return signalValue === literalValue && conn.isCustomValue(signalValue);
|
|
7979
7997
|
});
|
|
7980
7998
|
|
|
7981
7999
|
return parentCanProvide || userCanProvide || systemCanProvide;
|
|
@@ -8027,10 +8045,8 @@ const createRoutePattern = (pattern) => {
|
|
|
8027
8045
|
|
|
8028
8046
|
// If not in params, check signals
|
|
8029
8047
|
if (parentParamValue === undefined) {
|
|
8030
|
-
const parentConnection =
|
|
8031
|
-
|
|
8032
|
-
);
|
|
8033
|
-
if (parentConnection && parentConnection.signal) {
|
|
8048
|
+
const parentConnection = connectionMap.get(paramName);
|
|
8049
|
+
if (parentConnection) {
|
|
8034
8050
|
parentParamValue = parentConnection.signal.value;
|
|
8035
8051
|
}
|
|
8036
8052
|
}
|
|
@@ -8115,16 +8131,12 @@ const createRoutePattern = (pattern) => {
|
|
|
8115
8131
|
paramName = item.paramName;
|
|
8116
8132
|
paramValue = item.userValue;
|
|
8117
8133
|
} else {
|
|
8118
|
-
|
|
8119
|
-
|
|
8134
|
+
paramName = item.paramName;
|
|
8135
|
+
paramValue = item.signal.value;
|
|
8120
8136
|
// Only include custom parent signal values (not using defaults)
|
|
8121
|
-
if (
|
|
8122
|
-
signal?.value === undefined ||
|
|
8123
|
-
!options.isCustomValue?.(signal.value)
|
|
8124
|
-
) {
|
|
8137
|
+
if (paramValue === undefined || !item.isCustomValue(paramValue)) {
|
|
8125
8138
|
return { isCompatible: true, shouldInclude: false };
|
|
8126
8139
|
}
|
|
8127
|
-
paramValue = signal.value;
|
|
8128
8140
|
}
|
|
8129
8141
|
|
|
8130
8142
|
// Check if parameter value matches a literal segment in child pattern
|
|
@@ -8144,9 +8156,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8144
8156
|
|
|
8145
8157
|
// ROBUST FIX: For path parameters, check semantic compatibility by verifying
|
|
8146
8158
|
// that parent parameter values can actually produce the child route structure
|
|
8147
|
-
const isParentPathParam =
|
|
8148
|
-
(conn) => conn.paramName === paramName,
|
|
8149
|
-
);
|
|
8159
|
+
const isParentPathParam = connectionMap.has(paramName);
|
|
8150
8160
|
if (isParentPathParam) {
|
|
8151
8161
|
// Check if parent parameter value matches any child literal where it should
|
|
8152
8162
|
// The key insight: if parent has a specific parameter value, child route must
|
|
@@ -8180,9 +8190,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8180
8190
|
// Check for generic parameter-literal conflicts (only for path parameters)
|
|
8181
8191
|
if (!matchesChildLiteral) {
|
|
8182
8192
|
// Check if this is a path parameter from parent pattern
|
|
8183
|
-
const isParentPathParam =
|
|
8184
|
-
(conn) => conn.paramName === paramName,
|
|
8185
|
-
);
|
|
8193
|
+
const isParentPathParam = connectionMap.has(paramName);
|
|
8186
8194
|
if (isParentPathParam) {
|
|
8187
8195
|
// Parameter value (from user or signal) doesn't match this child's literals
|
|
8188
8196
|
// Check if child has any literal segments that would conflict with this parameter
|
|
@@ -8217,49 +8225,41 @@ const createRoutePattern = (pattern) => {
|
|
|
8217
8225
|
// CRITICAL: Check if user explicitly passed undefined for parameters that would
|
|
8218
8226
|
// normally be used to select this child route via sibling route relationships
|
|
8219
8227
|
for (const [paramName, paramValue] of Object.entries(params)) {
|
|
8220
|
-
if (paramValue
|
|
8221
|
-
|
|
8222
|
-
|
|
8228
|
+
if (paramValue !== undefined) {
|
|
8229
|
+
continue;
|
|
8230
|
+
}
|
|
8223
8231
|
|
|
8224
|
-
|
|
8225
|
-
|
|
8232
|
+
// Look for sibling routes (other children of the same parent) that use this parameter
|
|
8233
|
+
const siblingPatternObjs = patternObject.children;
|
|
8234
|
+
for (const siblingPatternObj of siblingPatternObjs) {
|
|
8235
|
+
if (siblingPatternObj === childPatternObj) continue; // Skip self
|
|
8226
8236
|
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8237
|
+
// Check if sibling route uses this parameter and get the connection
|
|
8238
|
+
const siblingConnection =
|
|
8239
|
+
siblingPatternObj.connectionMap.get(paramName);
|
|
8240
|
+
if (!siblingConnection) {
|
|
8241
|
+
continue;
|
|
8242
|
+
}
|
|
8243
|
+
const siblingSignalValue = siblingConnection.signal.value;
|
|
8244
|
+
if (siblingSignalValue === undefined) {
|
|
8245
|
+
continue;
|
|
8246
|
+
}
|
|
8247
|
+
// Check if this child route has a literal that matches the signal value
|
|
8248
|
+
const signalMatchesThisChildLiteral =
|
|
8249
|
+
childPatternObj.pattern.segments.some(
|
|
8250
|
+
(segment) =>
|
|
8251
|
+
segment.type === "literal" &&
|
|
8252
|
+
segment.value === siblingSignalValue,
|
|
8230
8253
|
);
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8254
|
+
if (signalMatchesThisChildLiteral) {
|
|
8255
|
+
// This child route's literal matches the sibling's signal value
|
|
8256
|
+
// User passed undefined to override that signal - don't use this child route
|
|
8257
|
+
if (DEBUG$2) {
|
|
8258
|
+
console.debug(
|
|
8259
|
+
`[${pattern}] Blocking child route ${childPatternObj.originalPattern} because ${paramName}:undefined overrides sibling signal value "${siblingSignalValue}"`,
|
|
8236
8260
|
);
|
|
8237
|
-
|
|
8238
|
-
if (
|
|
8239
|
-
siblingConnection &&
|
|
8240
|
-
siblingConnection.signal?.value !== undefined
|
|
8241
|
-
) {
|
|
8242
|
-
const signalValue = siblingConnection.signal.value;
|
|
8243
|
-
|
|
8244
|
-
// Check if this child route has a literal that matches the signal value
|
|
8245
|
-
const signalMatchesThisChildLiteral =
|
|
8246
|
-
childPatternObj.pattern.segments.some(
|
|
8247
|
-
(segment) =>
|
|
8248
|
-
segment.type === "literal" && segment.value === signalValue,
|
|
8249
|
-
);
|
|
8250
|
-
|
|
8251
|
-
if (signalMatchesThisChildLiteral) {
|
|
8252
|
-
// This child route's literal matches the sibling's signal value
|
|
8253
|
-
// User passed undefined to override that signal - don't use this child route
|
|
8254
|
-
if (DEBUG$2) {
|
|
8255
|
-
console.debug(
|
|
8256
|
-
`[${pattern}] Blocking child route ${childPatternObj.originalPattern} because ${paramName}:undefined overrides sibling signal value "${signalValue}"`,
|
|
8257
|
-
);
|
|
8258
|
-
}
|
|
8259
|
-
return false;
|
|
8260
|
-
}
|
|
8261
|
-
}
|
|
8262
8261
|
}
|
|
8262
|
+
return false;
|
|
8263
8263
|
}
|
|
8264
8264
|
}
|
|
8265
8265
|
}
|
|
@@ -8268,9 +8268,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8268
8268
|
let hasActiveParams = false;
|
|
8269
8269
|
const childParams = { ...compatibility.childParams };
|
|
8270
8270
|
|
|
8271
|
-
for (const connection of childPatternObj.
|
|
8272
|
-
const { paramName, signal, options } = connection;
|
|
8273
|
-
|
|
8271
|
+
for (const [paramName, connection] of childPatternObj.connectionMap) {
|
|
8274
8272
|
// Check if parameter was explicitly provided by user
|
|
8275
8273
|
const hasExplicitParam = paramName in params;
|
|
8276
8274
|
const explicitValue = params[paramName];
|
|
@@ -8280,15 +8278,18 @@ const createRoutePattern = (pattern) => {
|
|
|
8280
8278
|
childParams[paramName] = explicitValue;
|
|
8281
8279
|
if (
|
|
8282
8280
|
explicitValue !== undefined &&
|
|
8283
|
-
|
|
8281
|
+
connection.isCustomValue(explicitValue)
|
|
8284
8282
|
) {
|
|
8285
8283
|
hasActiveParams = true;
|
|
8286
8284
|
}
|
|
8287
|
-
} else
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8285
|
+
} else {
|
|
8286
|
+
const signalValue = connection.signal.value;
|
|
8287
|
+
if (signalValue !== undefined) {
|
|
8288
|
+
// No explicit override - use signal value
|
|
8289
|
+
childParams[paramName] = signalValue;
|
|
8290
|
+
if (connection.isCustomValue(signalValue)) {
|
|
8291
|
+
hasActiveParams = true;
|
|
8292
|
+
}
|
|
8292
8293
|
}
|
|
8293
8294
|
}
|
|
8294
8295
|
}
|
|
@@ -8313,19 +8314,17 @@ const createRoutePattern = (pattern) => {
|
|
|
8313
8314
|
if (value === undefined) return false;
|
|
8314
8315
|
|
|
8315
8316
|
// Check if this parameter has a default value in child's connections
|
|
8316
|
-
const childConnection = childPatternObj.
|
|
8317
|
-
(conn) => conn.paramName === paramName,
|
|
8318
|
-
);
|
|
8317
|
+
const childConnection = childPatternObj.connectionMap.get(paramName);
|
|
8319
8318
|
if (childConnection) {
|
|
8320
|
-
|
|
8319
|
+
const childDefault = childConnection.getDefaultValue();
|
|
8320
|
+
return value !== childDefault;
|
|
8321
8321
|
}
|
|
8322
8322
|
|
|
8323
8323
|
// Check if this parameter has a default value in parent's connections (current pattern)
|
|
8324
|
-
const parentConnection =
|
|
8325
|
-
(conn) => conn.paramName === paramName,
|
|
8326
|
-
);
|
|
8324
|
+
const parentConnection = connectionMap.get(paramName);
|
|
8327
8325
|
if (parentConnection) {
|
|
8328
|
-
|
|
8326
|
+
const parentDefault = parentConnection.getDefaultValue();
|
|
8327
|
+
return value !== parentDefault;
|
|
8329
8328
|
}
|
|
8330
8329
|
|
|
8331
8330
|
return true; // Non-connection parameters are considered non-default
|
|
@@ -8366,18 +8365,17 @@ const createRoutePattern = (pattern) => {
|
|
|
8366
8365
|
|
|
8367
8366
|
// Check if parameters that determine child selection are non-default
|
|
8368
8367
|
// OR if any descendant parameters indicate explicit navigation
|
|
8369
|
-
for (const connection of
|
|
8370
|
-
const
|
|
8371
|
-
const defaultValue = parameterDefaults.get(paramName);
|
|
8368
|
+
for (const [paramName, connection] of connectionMap) {
|
|
8369
|
+
const currentDefault = connection.getDefaultValue(); // Use current dynamic default
|
|
8372
8370
|
const resolvedValue = resolvedParams[paramName];
|
|
8373
8371
|
const userProvidedParam = paramName in params;
|
|
8374
8372
|
|
|
8375
|
-
if (extraLiterals.includes(
|
|
8373
|
+
if (extraLiterals.includes(currentDefault)) {
|
|
8376
8374
|
// This literal corresponds to a parameter in the parent
|
|
8377
8375
|
if (
|
|
8378
8376
|
userProvidedParam ||
|
|
8379
8377
|
(resolvedValue !== undefined &&
|
|
8380
|
-
|
|
8378
|
+
connection.isCustomValue(resolvedValue))
|
|
8381
8379
|
) {
|
|
8382
8380
|
// Parameter was explicitly provided or has custom value - child is needed
|
|
8383
8381
|
childSpecificParamsAreDefaults = false;
|
|
@@ -8392,7 +8390,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8392
8390
|
if (childSpecificParamsAreDefaults) {
|
|
8393
8391
|
for (const childConnection of childPatternObj.connections) {
|
|
8394
8392
|
const childParamName = childConnection.paramName;
|
|
8395
|
-
const childDefaultValue = childConnection.
|
|
8393
|
+
const childDefaultValue = childConnection.getDefaultValue();
|
|
8396
8394
|
const childResolvedValue = resolvedParams[childParamName];
|
|
8397
8395
|
|
|
8398
8396
|
// Only consider path parameters, not query parameters
|
|
@@ -8420,19 +8418,18 @@ const createRoutePattern = (pattern) => {
|
|
|
8420
8418
|
// When structural parameters (those that determine child selection) are defaults,
|
|
8421
8419
|
// prefer parent route regardless of whether child has other non-default parameters
|
|
8422
8420
|
if (childSpecificParamsAreDefaults) {
|
|
8423
|
-
for (const connection of
|
|
8424
|
-
const
|
|
8425
|
-
const defaultValue = parameterDefaults.get(paramName);
|
|
8421
|
+
for (const [paramName, connection] of connectionMap) {
|
|
8422
|
+
const currentDefault = connection.getDefaultValue(); // Use current dynamic default
|
|
8426
8423
|
const userProvidedParam = paramName in params;
|
|
8427
8424
|
|
|
8428
|
-
if (extraLiterals.includes(
|
|
8425
|
+
if (extraLiterals.includes(currentDefault) && !userProvidedParam) {
|
|
8429
8426
|
// This child includes a literal that represents a default value
|
|
8430
8427
|
// AND user didn't explicitly provide this parameter
|
|
8431
8428
|
// When structural parameters are defaults, prefer parent for cleaner URL
|
|
8432
8429
|
shouldUse = false;
|
|
8433
8430
|
if (DEBUG$2) {
|
|
8434
8431
|
console.debug(
|
|
8435
|
-
`[${pattern}] Preferring parent over child - child includes default literal '${
|
|
8432
|
+
`[${pattern}] Preferring parent over child - child includes default literal '${currentDefault}' for param '${paramName}' (structural parameter is default)`,
|
|
8436
8433
|
);
|
|
8437
8434
|
}
|
|
8438
8435
|
break;
|
|
@@ -8465,9 +8462,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8465
8462
|
) => {
|
|
8466
8463
|
// Start with child signal values
|
|
8467
8464
|
const baseParams = {};
|
|
8468
|
-
for (const connection of childPatternObj.
|
|
8469
|
-
const { paramName, signal, options } = connection;
|
|
8470
|
-
|
|
8465
|
+
for (const [paramName, connection] of childPatternObj.connectionMap) {
|
|
8471
8466
|
// Check if parameter was explicitly provided by user
|
|
8472
8467
|
const hasExplicitParam = paramName in params;
|
|
8473
8468
|
const explicitValue = params[paramName];
|
|
@@ -8478,12 +8473,15 @@ const createRoutePattern = (pattern) => {
|
|
|
8478
8473
|
baseParams[paramName] = explicitValue;
|
|
8479
8474
|
}
|
|
8480
8475
|
// If explicitly undefined, don't include it (which means don't use child route)
|
|
8481
|
-
} else
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8476
|
+
} else {
|
|
8477
|
+
const signalValue = connection.signal.value;
|
|
8478
|
+
if (
|
|
8479
|
+
signalValue !== undefined &&
|
|
8480
|
+
connection.isCustomValue(signalValue)
|
|
8481
|
+
) {
|
|
8482
|
+
// No explicit override - use signal value if non-default
|
|
8483
|
+
baseParams[paramName] = signalValue;
|
|
8484
|
+
}
|
|
8487
8485
|
}
|
|
8488
8486
|
}
|
|
8489
8487
|
|
|
@@ -8497,13 +8495,10 @@ const createRoutePattern = (pattern) => {
|
|
|
8497
8495
|
|
|
8498
8496
|
// Add parent's signal parameters
|
|
8499
8497
|
for (const connection of parentPatternObj.connections) {
|
|
8500
|
-
const { paramName
|
|
8498
|
+
const { paramName } = connection;
|
|
8501
8499
|
|
|
8502
8500
|
// Skip if child route already handles this parameter
|
|
8503
|
-
|
|
8504
|
-
(conn) => conn.paramName === paramName,
|
|
8505
|
-
);
|
|
8506
|
-
if (childConnection) {
|
|
8501
|
+
if (childPatternObj.connectionMap.has(paramName)) {
|
|
8507
8502
|
continue; // Child route handles this parameter directly
|
|
8508
8503
|
}
|
|
8509
8504
|
|
|
@@ -8512,18 +8507,19 @@ const createRoutePattern = (pattern) => {
|
|
|
8512
8507
|
continue; // Already have this parameter
|
|
8513
8508
|
}
|
|
8514
8509
|
|
|
8510
|
+
const signalValue = connection.signal.value;
|
|
8515
8511
|
// Only include custom signal values (not using defaults)
|
|
8516
8512
|
if (
|
|
8517
|
-
|
|
8518
|
-
|
|
8513
|
+
signalValue !== undefined &&
|
|
8514
|
+
connection.isCustomValue(signalValue)
|
|
8519
8515
|
) {
|
|
8520
8516
|
// Skip if parameter is consumed by child's literal path segments
|
|
8521
8517
|
const isConsumedByChildPath = childPatternObj.pattern.segments.some(
|
|
8522
8518
|
(segment) =>
|
|
8523
|
-
segment.type === "literal" && segment.value ===
|
|
8519
|
+
segment.type === "literal" && segment.value === signalValue,
|
|
8524
8520
|
);
|
|
8525
8521
|
if (!isConsumedByChildPath) {
|
|
8526
|
-
baseParams[paramName] =
|
|
8522
|
+
baseParams[paramName] = signalValue;
|
|
8527
8523
|
}
|
|
8528
8524
|
}
|
|
8529
8525
|
}
|
|
@@ -8545,10 +8541,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8545
8541
|
}
|
|
8546
8542
|
|
|
8547
8543
|
// Skip if child route already handles this parameter
|
|
8548
|
-
|
|
8549
|
-
(conn) => conn.paramName === paramName,
|
|
8550
|
-
);
|
|
8551
|
-
if (childConnection) {
|
|
8544
|
+
if (childPatternObj.connectionMap.has(paramName)) {
|
|
8552
8545
|
continue; // Child route handles this parameter directly
|
|
8553
8546
|
}
|
|
8554
8547
|
|
|
@@ -8562,10 +8555,10 @@ const createRoutePattern = (pattern) => {
|
|
|
8562
8555
|
}
|
|
8563
8556
|
|
|
8564
8557
|
// Check if parent parameter is at default value
|
|
8565
|
-
const parentConnection =
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8558
|
+
const parentConnection = connectionMap.get(paramName);
|
|
8559
|
+
const parentDefault = parentConnection
|
|
8560
|
+
? parentConnection.getDefaultValue()
|
|
8561
|
+
: undefined;
|
|
8569
8562
|
if (parentValue === parentDefault) {
|
|
8570
8563
|
continue; // Don't inherit default values
|
|
8571
8564
|
}
|
|
@@ -8576,15 +8569,11 @@ const createRoutePattern = (pattern) => {
|
|
|
8576
8569
|
|
|
8577
8570
|
// Apply user params with filtering logic
|
|
8578
8571
|
for (const [paramName, userValue] of Object.entries(params)) {
|
|
8579
|
-
const childConnection = childPatternObj.
|
|
8580
|
-
(conn) => conn.paramName === paramName,
|
|
8581
|
-
);
|
|
8572
|
+
const childConnection = childPatternObj.connectionMap.get(paramName);
|
|
8582
8573
|
|
|
8583
8574
|
if (childConnection) {
|
|
8584
|
-
const { options } = childConnection;
|
|
8585
|
-
|
|
8586
8575
|
// Only include if it's a custom value (not default)
|
|
8587
|
-
if (
|
|
8576
|
+
if (childConnection.isCustomValue(userValue)) {
|
|
8588
8577
|
baseParams[paramName] = userValue;
|
|
8589
8578
|
} else {
|
|
8590
8579
|
// User provided the default value - complete omission
|
|
@@ -8641,10 +8630,9 @@ const createRoutePattern = (pattern) => {
|
|
|
8641
8630
|
|
|
8642
8631
|
if (childParent && childParent.originalPattern === pattern) {
|
|
8643
8632
|
// Check if child has any non-default signal values
|
|
8644
|
-
const hasNonDefaultChildParams =
|
|
8633
|
+
const hasNonDefaultChildParams = childPatternObj.connections.some(
|
|
8645
8634
|
(childConnection) => {
|
|
8646
|
-
|
|
8647
|
-
return options.isCustomValue?.(signal?.value);
|
|
8635
|
+
return childConnection.isCustomValue(childConnection.signal.value);
|
|
8648
8636
|
},
|
|
8649
8637
|
);
|
|
8650
8638
|
|
|
@@ -8860,9 +8848,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8860
8848
|
(qp) => qp.name === connection.paramName,
|
|
8861
8849
|
);
|
|
8862
8850
|
// Allow non-default query parameters, but not path parameters
|
|
8863
|
-
return (
|
|
8864
|
-
!isQueryParam && connection.options.isCustomValue?.(resolvedValue)
|
|
8865
|
-
);
|
|
8851
|
+
return !isQueryParam && connection.isCustomValue(resolvedValue);
|
|
8866
8852
|
});
|
|
8867
8853
|
|
|
8868
8854
|
if (hasNonDefaultPathParams) {
|
|
@@ -8892,7 +8878,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8892
8878
|
// For non-immediate parents, only allow optimization if all resolved parameters have default values
|
|
8893
8879
|
const hasNonDefaultParameters = connections.some((connection) => {
|
|
8894
8880
|
const resolvedValue = resolvedParams[connection.paramName];
|
|
8895
|
-
return connection.
|
|
8881
|
+
return connection.isCustomValue(resolvedValue);
|
|
8896
8882
|
});
|
|
8897
8883
|
|
|
8898
8884
|
if (hasNonDefaultParameters) {
|
|
@@ -9070,7 +9056,7 @@ const createRoutePattern = (pattern) => {
|
|
|
9070
9056
|
const connection = targetAncestor.connections.find(
|
|
9071
9057
|
(conn) => conn.paramName === param.name,
|
|
9072
9058
|
);
|
|
9073
|
-
if (!connection || connection.
|
|
9059
|
+
if (!connection || connection.getDefaultValue() !== segment) {
|
|
9074
9060
|
if (DEBUG$2) {
|
|
9075
9061
|
console.debug(
|
|
9076
9062
|
`[${pattern}] tryDirectOptimization: Parameter default mismatch for ${param.name}`,
|
|
@@ -9117,10 +9103,8 @@ const createRoutePattern = (pattern) => {
|
|
|
9117
9103
|
// Include parameters that target pattern specifically needs
|
|
9118
9104
|
if (targetQueryParamNames.has(paramName)) {
|
|
9119
9105
|
// Only include if the value is not the default value
|
|
9120
|
-
const connection = targetAncestor.
|
|
9121
|
-
|
|
9122
|
-
);
|
|
9123
|
-
if (connection && connection.options.defaultValue !== value) {
|
|
9106
|
+
const connection = targetAncestor.connectionMap.get(paramName);
|
|
9107
|
+
if (connection && connection.getDefaultValue() !== value) {
|
|
9124
9108
|
ancestorParams[paramName] = value;
|
|
9125
9109
|
if (DEBUG$2) {
|
|
9126
9110
|
console.debug(
|
|
@@ -9135,7 +9119,7 @@ const createRoutePattern = (pattern) => {
|
|
|
9135
9119
|
const connection = sourceConnections.find(
|
|
9136
9120
|
(conn) => conn.paramName === paramName,
|
|
9137
9121
|
);
|
|
9138
|
-
if (connection && connection.
|
|
9122
|
+
if (connection && connection.getDefaultValue() !== value) {
|
|
9139
9123
|
ancestorParams[paramName] = value;
|
|
9140
9124
|
if (DEBUG$2) {
|
|
9141
9125
|
console.debug(
|
|
@@ -9160,14 +9144,14 @@ const createRoutePattern = (pattern) => {
|
|
|
9160
9144
|
|
|
9161
9145
|
// Also check target ancestor's own signal values for parameters not in resolvedParams
|
|
9162
9146
|
for (const connection of targetAncestor.connections) {
|
|
9163
|
-
const { paramName
|
|
9147
|
+
const { paramName } = connection;
|
|
9148
|
+
if (paramName in ancestorParams) {
|
|
9149
|
+
continue;
|
|
9150
|
+
}
|
|
9164
9151
|
|
|
9165
9152
|
// Only include if not already processed and has custom value (not default)
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
signal?.value !== undefined &&
|
|
9169
|
-
options.isCustomValue?.(signal.value)
|
|
9170
|
-
) {
|
|
9153
|
+
const signalValue = connection.signal.value;
|
|
9154
|
+
if (signalValue !== undefined && connection.isCustomValue(signalValue)) {
|
|
9171
9155
|
// Don't include path parameters that correspond to literal segments we're optimizing away
|
|
9172
9156
|
const targetParam = targetParams.find((p) => p.name === paramName);
|
|
9173
9157
|
const isPathParam = targetParam !== undefined; // Any param in segments is a path param
|
|
@@ -9175,16 +9159,16 @@ const createRoutePattern = (pattern) => {
|
|
|
9175
9159
|
// Skip path parameters - we want them to use default values for optimization
|
|
9176
9160
|
if (DEBUG$2) {
|
|
9177
9161
|
console.debug(
|
|
9178
|
-
`[${pattern}] tryDirectOptimization: Skipping path param ${paramName}=${
|
|
9162
|
+
`[${pattern}] tryDirectOptimization: Skipping path param ${paramName}=${signalValue} (will use default)`,
|
|
9179
9163
|
);
|
|
9180
9164
|
}
|
|
9181
9165
|
continue;
|
|
9182
9166
|
}
|
|
9183
9167
|
|
|
9184
|
-
ancestorParams[paramName] =
|
|
9168
|
+
ancestorParams[paramName] = signalValue;
|
|
9185
9169
|
if (DEBUG$2) {
|
|
9186
9170
|
console.debug(
|
|
9187
|
-
`[${pattern}] tryDirectOptimization: Added target signal param ${paramName}=${
|
|
9171
|
+
`[${pattern}] tryDirectOptimization: Added target signal param ${paramName}=${signalValue}`,
|
|
9188
9172
|
);
|
|
9189
9173
|
}
|
|
9190
9174
|
}
|
|
@@ -9195,24 +9179,25 @@ const createRoutePattern = (pattern) => {
|
|
|
9195
9179
|
|
|
9196
9180
|
while (currentParent) {
|
|
9197
9181
|
for (const connection of currentParent.connections) {
|
|
9198
|
-
const { paramName
|
|
9182
|
+
const { paramName } = connection;
|
|
9183
|
+
if (paramName in ancestorParams) {
|
|
9184
|
+
continue;
|
|
9185
|
+
}
|
|
9199
9186
|
|
|
9200
9187
|
// Only inherit custom values (not defaults) that we don't already have
|
|
9188
|
+
const signalValue = connection.signal.value;
|
|
9201
9189
|
if (
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
options.isCustomValue?.(signal.value)
|
|
9190
|
+
signalValue !== undefined &&
|
|
9191
|
+
connection.isCustomValue(signalValue)
|
|
9205
9192
|
) {
|
|
9206
9193
|
// Check if this parameter would be redundant with target ancestor's literal segments
|
|
9207
9194
|
const isRedundant = isParameterRedundantWithLiteralSegments(
|
|
9208
9195
|
targetAncestor.pattern,
|
|
9209
9196
|
currentParent.pattern,
|
|
9210
|
-
paramName
|
|
9211
|
-
signal.value,
|
|
9212
|
-
);
|
|
9197
|
+
paramName);
|
|
9213
9198
|
|
|
9214
9199
|
if (!isRedundant) {
|
|
9215
|
-
ancestorParams[paramName] =
|
|
9200
|
+
ancestorParams[paramName] = signalValue;
|
|
9216
9201
|
}
|
|
9217
9202
|
}
|
|
9218
9203
|
}
|
|
@@ -9275,24 +9260,25 @@ const createRoutePattern = (pattern) => {
|
|
|
9275
9260
|
while (currentParent) {
|
|
9276
9261
|
// Check parent's signal connections for non-default values to inherit
|
|
9277
9262
|
for (const parentConnection of currentParent.connections) {
|
|
9278
|
-
const { paramName
|
|
9263
|
+
const { paramName } = parentConnection;
|
|
9264
|
+
if (paramName in finalParams) {
|
|
9265
|
+
continue; // Already have this parameter
|
|
9266
|
+
}
|
|
9279
9267
|
|
|
9280
9268
|
// Only inherit if we don't have this param and parent has custom value (not default)
|
|
9269
|
+
const parentSignalValue = parentConnection.signal.value;
|
|
9281
9270
|
if (
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
options.isCustomValue?.(signal.value)
|
|
9271
|
+
parentSignalValue !== undefined &&
|
|
9272
|
+
parentConnection.isCustomValue(parentSignalValue)
|
|
9285
9273
|
) {
|
|
9286
9274
|
// Don't inherit if parameter corresponds to a literal in our path
|
|
9287
9275
|
const shouldInherit = !isParameterRedundantWithLiteralSegments(
|
|
9288
9276
|
parsedPattern,
|
|
9289
9277
|
currentParent.pattern,
|
|
9290
|
-
paramName
|
|
9291
|
-
signal.value,
|
|
9292
|
-
);
|
|
9278
|
+
paramName);
|
|
9293
9279
|
|
|
9294
9280
|
if (shouldInherit) {
|
|
9295
|
-
finalParams[paramName] =
|
|
9281
|
+
finalParams[paramName] = parentSignalValue;
|
|
9296
9282
|
}
|
|
9297
9283
|
}
|
|
9298
9284
|
}
|
|
@@ -9342,9 +9328,9 @@ const createRoutePattern = (pattern) => {
|
|
|
9342
9328
|
urlPatternRaw: pattern,
|
|
9343
9329
|
cleanPattern,
|
|
9344
9330
|
connections,
|
|
9331
|
+
connectionMap,
|
|
9345
9332
|
parsedPattern,
|
|
9346
9333
|
signalSet,
|
|
9347
|
-
parameterDefaults, // Add parameterDefaults for signal clearing logic
|
|
9348
9334
|
children: [],
|
|
9349
9335
|
parent: null,
|
|
9350
9336
|
depth: 0, // Will be calculated after relationships are built
|
|
@@ -9412,11 +9398,7 @@ const canParameterReachChildRoute = (
|
|
|
9412
9398
|
/**
|
|
9413
9399
|
* Parse a route pattern string into structured segments
|
|
9414
9400
|
*/
|
|
9415
|
-
const parsePattern = (
|
|
9416
|
-
pattern,
|
|
9417
|
-
parameterDefaults = new Map(),
|
|
9418
|
-
connections = [],
|
|
9419
|
-
) => {
|
|
9401
|
+
const parsePattern = (pattern, connectionMap) => {
|
|
9420
9402
|
// Handle root route
|
|
9421
9403
|
if (pattern === "/") {
|
|
9422
9404
|
return {
|
|
@@ -9490,18 +9472,18 @@ const parsePattern = (
|
|
|
9490
9472
|
// 1. Explicitly marked with ?
|
|
9491
9473
|
// 2. Has a default value
|
|
9492
9474
|
// 3. Connected signal has undefined value and no explicit default (allows /map to match /map/:panel)
|
|
9493
|
-
|
|
9475
|
+
const connection = connectionMap.get(paramName);
|
|
9476
|
+
const hasDefault =
|
|
9477
|
+
connection && connection.getDefaultValue() !== undefined;
|
|
9478
|
+
let isOptional = seg.endsWith("?") || hasDefault;
|
|
9494
9479
|
|
|
9495
9480
|
if (!isOptional) {
|
|
9496
9481
|
// Check if connected signal has undefined value (making parameter optional for index routes)
|
|
9497
|
-
const connection = connections.find(
|
|
9498
|
-
(conn) => conn.paramName === paramName,
|
|
9499
|
-
);
|
|
9500
9482
|
if (
|
|
9501
9483
|
connection &&
|
|
9502
9484
|
connection.signal &&
|
|
9503
9485
|
connection.signal.value === undefined &&
|
|
9504
|
-
!
|
|
9486
|
+
!hasDefault
|
|
9505
9487
|
) {
|
|
9506
9488
|
isOptional = true;
|
|
9507
9489
|
}
|
|
@@ -9544,7 +9526,7 @@ const checkIfLiteralCanBeOptionalWithPatternObj = (
|
|
|
9544
9526
|
|
|
9545
9527
|
// Check current pattern's connections
|
|
9546
9528
|
for (const connection of patternObj.connections) {
|
|
9547
|
-
if (connection.
|
|
9529
|
+
if (connection.getDefaultValue() === literalValue) {
|
|
9548
9530
|
return true;
|
|
9549
9531
|
}
|
|
9550
9532
|
}
|
|
@@ -9553,7 +9535,7 @@ const checkIfLiteralCanBeOptionalWithPatternObj = (
|
|
|
9553
9535
|
let currentParent = patternObj.parent;
|
|
9554
9536
|
while (currentParent) {
|
|
9555
9537
|
for (const connection of currentParent.connections) {
|
|
9556
|
-
if (connection.
|
|
9538
|
+
if (connection.getDefaultValue() === literalValue) {
|
|
9557
9539
|
return true;
|
|
9558
9540
|
}
|
|
9559
9541
|
}
|
|
@@ -9564,7 +9546,7 @@ const checkIfLiteralCanBeOptionalWithPatternObj = (
|
|
|
9564
9546
|
const checkChildrenRecursively = (pattern) => {
|
|
9565
9547
|
for (const child of pattern.children || []) {
|
|
9566
9548
|
for (const connection of child.connections) {
|
|
9567
|
-
if (connection.
|
|
9549
|
+
if (connection.getDefaultValue() === literalValue) {
|
|
9568
9550
|
return true;
|
|
9569
9551
|
}
|
|
9570
9552
|
}
|
|
@@ -9584,7 +9566,7 @@ const checkIfLiteralCanBeOptionalWithPatternObj = (
|
|
|
9584
9566
|
const matchUrl = (
|
|
9585
9567
|
parsedPattern,
|
|
9586
9568
|
url,
|
|
9587
|
-
{
|
|
9569
|
+
{ baseUrl, connections = [], patternObj = null },
|
|
9588
9570
|
) => {
|
|
9589
9571
|
// Parse the URL
|
|
9590
9572
|
const urlObj = new URL(url, baseUrl);
|
|
@@ -9668,23 +9650,15 @@ const matchUrl = (
|
|
|
9668
9650
|
if (urlSegmentIndex >= urlSegments.length) {
|
|
9669
9651
|
// No URL segment for this parameter
|
|
9670
9652
|
if (patternSeg.optional) {
|
|
9671
|
-
// Optional parameter -
|
|
9672
|
-
const defaultValue = parameterDefaults.get(patternSeg.name);
|
|
9673
|
-
if (defaultValue !== undefined) {
|
|
9674
|
-
params[patternSeg.name] = defaultValue;
|
|
9675
|
-
}
|
|
9653
|
+
// Optional parameter - don't add default here, let resolveParams handle it
|
|
9676
9654
|
continue;
|
|
9677
9655
|
}
|
|
9678
9656
|
// Required parameter missing - but check if we can use trailing slash logic
|
|
9679
9657
|
// If this is the last segment and we have a trailing slash difference, it might still match
|
|
9680
9658
|
const isLastSegment = i === parsedPattern.segments.length - 1;
|
|
9681
9659
|
if (isLastSegment && patternHasTrailingSlash && !urlHasTrailingSlash) {
|
|
9682
|
-
// Pattern expects trailing slash segment, URL doesn't have it
|
|
9683
|
-
|
|
9684
|
-
if (defaultValue !== undefined) {
|
|
9685
|
-
params[patternSeg.name] = defaultValue;
|
|
9686
|
-
continue;
|
|
9687
|
-
}
|
|
9660
|
+
// Pattern expects trailing slash segment, URL doesn't have it - allow missing optional param
|
|
9661
|
+
continue;
|
|
9688
9662
|
}
|
|
9689
9663
|
return null; // Required parameter missing
|
|
9690
9664
|
}
|
|
@@ -9700,8 +9674,7 @@ const matchUrl = (
|
|
|
9700
9674
|
// Patterns with trailing slashes can match additional URL segments (like wildcards)
|
|
9701
9675
|
// Patterns without trailing slashes should match exactly (unless they're wildcards)
|
|
9702
9676
|
// BUT: if pattern has children, it can also match additional segments (hierarchical matching)
|
|
9703
|
-
const hasChildren =
|
|
9704
|
-
patternObj && patternObj.children && patternObj.children.length > 0;
|
|
9677
|
+
const hasChildren = patternObj && patternObj.children.length > 0;
|
|
9705
9678
|
if (
|
|
9706
9679
|
!parsedPattern.wildcard &&
|
|
9707
9680
|
!parsedPattern.trailingSlash &&
|
|
@@ -9716,12 +9689,8 @@ const matchUrl = (
|
|
|
9716
9689
|
const searchParams = extractSearchParams(urlObj, connections);
|
|
9717
9690
|
Object.assign(params, searchParams);
|
|
9718
9691
|
|
|
9719
|
-
//
|
|
9720
|
-
|
|
9721
|
-
if (!(paramName in params)) {
|
|
9722
|
-
params[paramName] = defaultValue;
|
|
9723
|
-
}
|
|
9724
|
-
}
|
|
9692
|
+
// Don't add defaults here - rawParams should only contain what's in the URL
|
|
9693
|
+
// Defaults are handled by resolveParams() to create the final merged parameters
|
|
9725
9694
|
|
|
9726
9695
|
return params;
|
|
9727
9696
|
};
|
|
@@ -9735,8 +9704,8 @@ const extractSearchParams = (urlObj, connections = []) => {
|
|
|
9735
9704
|
// Create a map for quick signal type lookup
|
|
9736
9705
|
const signalTypes = new Map();
|
|
9737
9706
|
for (const connection of connections) {
|
|
9738
|
-
if (connection.
|
|
9739
|
-
signalTypes.set(connection.paramName, connection.
|
|
9707
|
+
if (connection.type) {
|
|
9708
|
+
signalTypes.set(connection.paramName, connection.type);
|
|
9740
9709
|
}
|
|
9741
9710
|
}
|
|
9742
9711
|
|
|
@@ -10181,9 +10150,7 @@ const setupPatterns = (patternDefinitions) => {
|
|
|
10181
10150
|
let parentPatternObj = currentPatternObj.parent;
|
|
10182
10151
|
while (parentPatternObj) {
|
|
10183
10152
|
for (const connection of parentPatternObj.connections) {
|
|
10184
|
-
|
|
10185
|
-
allRelevantSignals.add(connection.signal);
|
|
10186
|
-
}
|
|
10153
|
+
allRelevantSignals.add(connection.signal);
|
|
10187
10154
|
}
|
|
10188
10155
|
// Move up the parent chain
|
|
10189
10156
|
parentPatternObj = parentPatternObj.parent;
|
|
@@ -10194,9 +10161,7 @@ const setupPatterns = (patternDefinitions) => {
|
|
|
10194
10161
|
for (const childPatternObj of patternObj.children || []) {
|
|
10195
10162
|
// Add child's own signals
|
|
10196
10163
|
for (const connection of childPatternObj.connections) {
|
|
10197
|
-
|
|
10198
|
-
allRelevantSignals.add(connection.signal);
|
|
10199
|
-
}
|
|
10164
|
+
allRelevantSignals.add(connection.signal);
|
|
10200
10165
|
}
|
|
10201
10166
|
// Recursively add grandchildren signals
|
|
10202
10167
|
addDescendantSignals(childPatternObj);
|
|
@@ -10362,14 +10327,10 @@ const updateRoutes = (
|
|
|
10362
10327
|
newMatching,
|
|
10363
10328
|
} of routeMatchInfoSet) {
|
|
10364
10329
|
const { routePattern } = routePrivateProperties;
|
|
10365
|
-
const {
|
|
10330
|
+
const { connectionMap } = routePattern;
|
|
10366
10331
|
|
|
10367
|
-
for (const {
|
|
10368
|
-
signal:
|
|
10369
|
-
paramName,
|
|
10370
|
-
options = {},
|
|
10371
|
-
} of connections) {
|
|
10372
|
-
const { debug } = options;
|
|
10332
|
+
for (const [paramName, connection] of connectionMap) {
|
|
10333
|
+
const { signal: paramSignal, debug } = connection;
|
|
10373
10334
|
const params = routePrivateProperties.rawParamsSignal.value;
|
|
10374
10335
|
const urlParamValue = params[paramName];
|
|
10375
10336
|
|
|
@@ -10450,7 +10411,7 @@ const updateRoutes = (
|
|
|
10450
10411
|
// 2. AND no matching route extracts this parameter from URL
|
|
10451
10412
|
// 3. AND parameter has no default value (making it truly optional)
|
|
10452
10413
|
if (matchingRouteInSameFamily && !parameterExtractedByMatchingRoute) {
|
|
10453
|
-
const defaultValue =
|
|
10414
|
+
const defaultValue = connection.getDefaultValue();
|
|
10454
10415
|
if (defaultValue === undefined) {
|
|
10455
10416
|
// Parameter is not extracted within same family and has no default - reset it
|
|
10456
10417
|
if (debug) {
|
|
@@ -10458,21 +10419,21 @@ const updateRoutes = (
|
|
|
10458
10419
|
`[route] Same family navigation, ${paramName} not extracted and has no default: resetting signal`,
|
|
10459
10420
|
);
|
|
10460
10421
|
}
|
|
10461
|
-
|
|
10422
|
+
paramSignal.value = undefined;
|
|
10462
10423
|
} else if (debug) {
|
|
10463
10424
|
// Parameter has a default value - preserve current signal value
|
|
10464
10425
|
console.debug(
|
|
10465
|
-
`[route] Parameter ${paramName} has default value ${defaultValue}: preserving signal value: ${
|
|
10426
|
+
`[route] Parameter ${paramName} has default value ${defaultValue}: preserving signal value: ${paramSignal.value}`,
|
|
10466
10427
|
);
|
|
10467
10428
|
}
|
|
10468
10429
|
} else if (debug) {
|
|
10469
10430
|
if (!matchingRouteInSameFamily) {
|
|
10470
10431
|
console.debug(
|
|
10471
|
-
`[route] Different route family: preserving ${paramName} signal value: ${
|
|
10432
|
+
`[route] Different route family: preserving ${paramName} signal value: ${paramSignal.value}`,
|
|
10472
10433
|
);
|
|
10473
10434
|
} else {
|
|
10474
10435
|
console.debug(
|
|
10475
|
-
`[route] Parameter ${paramName} extracted by matching route: preserving signal value: ${
|
|
10436
|
+
`[route] Parameter ${paramName} extracted by matching route: preserving signal value: ${paramSignal.value}`,
|
|
10476
10437
|
);
|
|
10477
10438
|
}
|
|
10478
10439
|
}
|
|
@@ -10481,11 +10442,11 @@ const updateRoutes = (
|
|
|
10481
10442
|
|
|
10482
10443
|
// URL -> Signal sync: When route matches, ensure signal matches URL state
|
|
10483
10444
|
// URL is the source of truth for explicit parameters
|
|
10484
|
-
const value =
|
|
10445
|
+
const value = paramSignal.peek();
|
|
10485
10446
|
if (urlParamValue === undefined) {
|
|
10486
10447
|
// No URL parameter - reset signal to its current default value
|
|
10487
10448
|
// (handles both static fallback and dynamic default cases)
|
|
10488
|
-
const defaultValue =
|
|
10449
|
+
const defaultValue = connection.getDefaultValue();
|
|
10489
10450
|
if (value === defaultValue) {
|
|
10490
10451
|
// Signal already has correct default value, no sync needed
|
|
10491
10452
|
continue;
|
|
@@ -10495,7 +10456,7 @@ const updateRoutes = (
|
|
|
10495
10456
|
`[route] URL->Signal: ${paramName} not in URL, reset signal to default (${defaultValue})`,
|
|
10496
10457
|
);
|
|
10497
10458
|
}
|
|
10498
|
-
|
|
10459
|
+
paramSignal.value = defaultValue;
|
|
10499
10460
|
continue;
|
|
10500
10461
|
}
|
|
10501
10462
|
if (urlParamValue === value) {
|
|
@@ -10507,7 +10468,7 @@ const updateRoutes = (
|
|
|
10507
10468
|
`[route] URL->Signal: ${paramName}=${urlParamValue} in url, sync signal with url`,
|
|
10508
10469
|
);
|
|
10509
10470
|
}
|
|
10510
|
-
|
|
10471
|
+
paramSignal.value = urlParamValue;
|
|
10511
10472
|
continue;
|
|
10512
10473
|
}
|
|
10513
10474
|
}
|
|
@@ -10612,7 +10573,7 @@ const getRoutePrivateProperties = (route) => {
|
|
|
10612
10573
|
|
|
10613
10574
|
const registerRoute = (routePattern) => {
|
|
10614
10575
|
const urlPatternRaw = routePattern.originalPattern;
|
|
10615
|
-
const { cleanPattern,
|
|
10576
|
+
const { cleanPattern, connectionMap } = routePattern;
|
|
10616
10577
|
|
|
10617
10578
|
const cleanupCallbackSet = new Set();
|
|
10618
10579
|
const cleanup = () => {
|
|
@@ -10702,22 +10663,19 @@ const registerRoute = (routePattern) => {
|
|
|
10702
10663
|
}
|
|
10703
10664
|
});
|
|
10704
10665
|
|
|
10705
|
-
for (const
|
|
10706
|
-
const { debug } =
|
|
10666
|
+
for (const [paramName, connection] of connectionMap) {
|
|
10667
|
+
const { signal: paramSignal, debug } = connection;
|
|
10707
10668
|
|
|
10708
10669
|
if (debug) {
|
|
10709
10670
|
console.debug(
|
|
10710
10671
|
`[route] connecting url param "${paramName}" to signal`,
|
|
10711
|
-
|
|
10672
|
+
paramSignal,
|
|
10712
10673
|
);
|
|
10713
10674
|
}
|
|
10714
|
-
|
|
10715
|
-
// URL -> Signal synchronization now handled in updateRoutes() to eliminate circular dependency
|
|
10716
|
-
|
|
10717
10675
|
// Signal -> URL sync: When signal changes, update URL to reflect meaningful state
|
|
10718
10676
|
// Only sync non-default values to keep URLs clean (static fallbacks stay invisible)
|
|
10719
10677
|
effect(() => {
|
|
10720
|
-
const value =
|
|
10678
|
+
const value = paramSignal.value;
|
|
10721
10679
|
const params = rawParamsSignal.value;
|
|
10722
10680
|
const urlParamValue = params[paramName];
|
|
10723
10681
|
const matching = matchingSignal.value;
|
|
@@ -10728,7 +10686,7 @@ const registerRoute = (routePattern) => {
|
|
|
10728
10686
|
}
|
|
10729
10687
|
if (urlParamValue === undefined) {
|
|
10730
10688
|
// No URL parameter exists - check if signal has meaningful value to add
|
|
10731
|
-
const defaultValue =
|
|
10689
|
+
const defaultValue = connection.getDefaultValue();
|
|
10732
10690
|
if (value === defaultValue) {
|
|
10733
10691
|
// Signal using default value, keep URL clean (no parameter needed)
|
|
10734
10692
|
return;
|