@jsenv/navi 0.16.19 → 0.16.21

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.
@@ -9465,7 +9465,13 @@ const extractSearchParams = (urlObj, connections = []) => {
9465
9465
  const numberValue = Number(value);
9466
9466
  params[key] = isNaN(numberValue) ? value : numberValue;
9467
9467
  } else if (signalType === "boolean") {
9468
- params[key] = value === "true" || value === "1";
9468
+ // Handle boolean query parameters:
9469
+ // ?walk=true → true
9470
+ // ?walk=1 → true
9471
+ // ?walk → true (parameter present without value)
9472
+ // ?walk=false → false
9473
+ // ?walk=0 → false
9474
+ params[key] = value === "true" || value === "1" || value === "";
9469
9475
  } else {
9470
9476
  params[key] = value;
9471
9477
  }
@@ -10039,6 +10045,110 @@ const updateRoutes = (
10039
10045
  matchingRouteSet.add(route);
10040
10046
  }
10041
10047
  }
10048
+
10049
+ // URL -> Signal synchronization (moved from individual route effects to eliminate circular dependency)
10050
+ for (const {
10051
+ route,
10052
+ routePrivateProperties,
10053
+ newMatching,
10054
+ } of routeMatchInfoSet) {
10055
+ const { routePattern } = routePrivateProperties;
10056
+ const { connections } = routePattern;
10057
+
10058
+ for (const {
10059
+ signal: stateSignal,
10060
+ paramName,
10061
+ options = {},
10062
+ } of connections) {
10063
+ const { debug } = options;
10064
+ const params = routePrivateProperties.rawParamsSignal.value;
10065
+ const urlParamValue = params[paramName];
10066
+
10067
+ if (newMatching) {
10068
+ // When route matches, sync signal with URL parameter value
10069
+ // This ensures URL is the source of truth
10070
+ if (debug) {
10071
+ console.debug(
10072
+ `[route] Route matching: setting ${paramName} signal to URL value: ${urlParamValue}`,
10073
+ );
10074
+ }
10075
+ stateSignal.value = urlParamValue;
10076
+ } else {
10077
+ // When route doesn't match, check if we're navigating to a parent route
10078
+ let parentRouteMatching = false;
10079
+ for (const otherRoute of routeSet) {
10080
+ if (otherRoute === route || !otherRoute.matching) {
10081
+ continue;
10082
+ }
10083
+ const otherRouteProperties = getRoutePrivateProperties(otherRoute);
10084
+ const otherPatternObj = otherRouteProperties.routePattern;
10085
+
10086
+ // Check if the other route pattern is a parent of this route pattern
10087
+ // Using the built relationships in the pattern objects
10088
+ let currentParent = routePattern.parent;
10089
+ let foundParent = false;
10090
+ while (currentParent) {
10091
+ if (currentParent === otherPatternObj) {
10092
+ foundParent = true;
10093
+ break;
10094
+ }
10095
+ currentParent = currentParent.parent;
10096
+ }
10097
+
10098
+ if (!foundParent) {
10099
+ continue;
10100
+ }
10101
+
10102
+ // Found a parent route that's matching, but check if there's a more specific
10103
+ // sibling route also matching (indicating sibling navigation, not parent navigation)
10104
+ let hasMatchingSibling = false;
10105
+ for (const siblingCandidateRoute of routeSet) {
10106
+ if (
10107
+ siblingCandidateRoute === route ||
10108
+ siblingCandidateRoute === otherRoute ||
10109
+ !siblingCandidateRoute.matching
10110
+ ) {
10111
+ continue;
10112
+ }
10113
+
10114
+ const siblingProperties = getRoutePrivateProperties(
10115
+ siblingCandidateRoute,
10116
+ );
10117
+ const siblingPatternObj = siblingProperties.routePattern;
10118
+
10119
+ // Check if this is a sibling (shares the same parent)
10120
+ if (siblingPatternObj.parent === currentParent) {
10121
+ hasMatchingSibling = true;
10122
+ break;
10123
+ }
10124
+ }
10125
+
10126
+ // Only treat as parent navigation if no sibling is matching
10127
+ if (!hasMatchingSibling) {
10128
+ parentRouteMatching = true;
10129
+ break; // Found the parent route, no need to check other routes
10130
+ }
10131
+ }
10132
+
10133
+ if (parentRouteMatching) {
10134
+ // We're navigating to a parent route - clear this signal to reflect the hierarchy
10135
+ const defaultValue = routePattern.parameterDefaults?.get(paramName);
10136
+ if (debug) {
10137
+ console.debug(
10138
+ `[route] Parent route ${parentRouteMatching} matching: clearing ${paramName} signal to default: ${defaultValue}`,
10139
+ );
10140
+ }
10141
+ stateSignal.value = defaultValue;
10142
+ } else if (debug) {
10143
+ // We're navigating to a different route family - preserve signal for future URL building
10144
+ // Keep current signal value unchanged
10145
+ console.debug(
10146
+ `[route] Different route family: preserving ${paramName} signal value: ${stateSignal.value}`,
10147
+ );
10148
+ }
10149
+ }
10150
+ }
10151
+ }
10042
10152
  });
10043
10153
 
10044
10154
  // must be after paramsSignal.value update to ensure the proxy target is set
@@ -10230,102 +10340,7 @@ const registerRoute = (routePattern) => {
10230
10340
  );
10231
10341
  }
10232
10342
 
10233
- // URL -> Signal synchronization with parent-child route hierarchy checking
10234
- effect(() => {
10235
- const matching = matchingSignal.value;
10236
- const params = rawParamsSignal.value;
10237
- const urlParamValue = params[paramName];
10238
-
10239
- if (debug) {
10240
- console.debug(
10241
- `[route] URL->Signal effect triggered for ${paramName}: matching=${matching}, urlParamValue=${urlParamValue}, currentSignalValue=${stateSignal.value}`,
10242
- );
10243
- }
10244
-
10245
- if (matching) {
10246
- // When route matches, sync signal with URL parameter value
10247
- // This ensures URL is the source of truth
10248
- if (debug) {
10249
- console.debug(
10250
- `[route] Route matching: setting ${paramName} signal to URL value: ${urlParamValue}`,
10251
- );
10252
- }
10253
- stateSignal.value = urlParamValue;
10254
- } else {
10255
- // When route doesn't match, check if we're navigating to a parent route
10256
- let parentRouteMatching = false;
10257
- for (const otherRoute of routeSet) {
10258
- if (otherRoute === route || !otherRoute.matching) {
10259
- continue;
10260
- }
10261
- const otherRouteProperties = getRoutePrivateProperties(otherRoute);
10262
- const otherPatternObj = otherRouteProperties.routePattern;
10263
-
10264
- // Check if the other route pattern is a parent of this route pattern
10265
- // Using the built relationships in the pattern objects
10266
- let currentParent = routePattern.parent;
10267
- let foundParent = false;
10268
- while (currentParent) {
10269
- if (currentParent === otherPatternObj) {
10270
- foundParent = true;
10271
- break;
10272
- }
10273
- currentParent = currentParent.parent;
10274
- }
10275
-
10276
- if (!foundParent) {
10277
- continue;
10278
- }
10279
-
10280
- // Found a parent route that's matching, but check if there's a more specific
10281
- // sibling route also matching (indicating sibling navigation, not parent navigation)
10282
- let hasMatchingSibling = false;
10283
- for (const siblingCandidateRoute of routeSet) {
10284
- if (
10285
- siblingCandidateRoute === route ||
10286
- siblingCandidateRoute === otherRoute ||
10287
- !siblingCandidateRoute.matching
10288
- ) {
10289
- continue;
10290
- }
10291
-
10292
- const siblingProperties = getRoutePrivateProperties(
10293
- siblingCandidateRoute,
10294
- );
10295
- const siblingPatternObj = siblingProperties.routePattern;
10296
-
10297
- // Check if this is a sibling (shares the same parent)
10298
- if (siblingPatternObj.parent === currentParent) {
10299
- hasMatchingSibling = true;
10300
- break;
10301
- }
10302
- }
10303
-
10304
- // Only treat as parent navigation if no sibling is matching
10305
- if (!hasMatchingSibling) {
10306
- parentRouteMatching = true;
10307
- break; // Found the parent route, no need to check other routes
10308
- }
10309
- }
10310
-
10311
- if (parentRouteMatching) {
10312
- // We're navigating to a parent route - clear this signal to reflect the hierarchy
10313
- const defaultValue = routePattern.parameterDefaults?.get(paramName);
10314
- if (debug) {
10315
- console.debug(
10316
- `[route] Parent route ${parentRouteMatching} matching: clearing ${paramName} signal to default: ${defaultValue}`,
10317
- );
10318
- }
10319
- stateSignal.value = defaultValue;
10320
- } else if (debug) {
10321
- // We're navigating to a different route family - preserve signal for future URL building
10322
- // Keep current signal value unchanged
10323
- console.debug(
10324
- `[route] Different route family: preserving ${paramName} signal value: ${stateSignal.value}`,
10325
- );
10326
- }
10327
- }
10328
- });
10343
+ // URL -> Signal synchronization now handled in updateRoutes() to eliminate circular dependency
10329
10344
 
10330
10345
  // Signal -> URL synchronization
10331
10346
  effect(() => {