@jsenv/navi 0.16.0 → 0.16.1

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.
@@ -7793,6 +7793,41 @@ const createRoutePattern = (pattern) => {
7793
7793
  }
7794
7794
  }
7795
7795
 
7796
+ // PARENT PARAMETER INHERITANCE: Inherit query parameters from parent patterns
7797
+ // This allows child routes like "/map/isochrone" to inherit "zoom=15" from parent "/map/?zoom=..."
7798
+ const parentPatterns = relationships?.parentPatterns || [];
7799
+ for (const parentPattern of parentPatterns) {
7800
+ const parentPatternData = getPatternData(parentPattern);
7801
+ if (!parentPatternData) continue;
7802
+
7803
+ // Check parent's signal connections for non-default values to inherit
7804
+ for (const parentConnection of parentPatternData.connections) {
7805
+ const { paramName, signal, options } = parentConnection;
7806
+ const defaultValue = options.defaultValue;
7807
+
7808
+ // If we don't already have this parameter and parent signal has non-default value
7809
+ if (
7810
+ !(paramName in finalParams) &&
7811
+ signal?.value !== undefined &&
7812
+ signal.value !== defaultValue
7813
+ ) {
7814
+ // Check if this parameter corresponds to a literal segment in our path
7815
+ // E.g., don't inherit "section=analytics" if our path is "/admin/analytics"
7816
+ const shouldInherit = !isParameterRedundantWithLiteralSegments(
7817
+ parsedPattern,
7818
+ parentPatternData.parsedPattern,
7819
+ paramName,
7820
+ signal.value,
7821
+ );
7822
+
7823
+ if (shouldInherit) {
7824
+ // Inherit the parent's signal value
7825
+ finalParams[paramName] = signal.value;
7826
+ }
7827
+ }
7828
+ }
7829
+ }
7830
+
7796
7831
  if (!parsedPattern.segments) {
7797
7832
  return "/";
7798
7833
  }
@@ -8157,6 +8192,27 @@ const buildUrlFromPattern = (parsedPattern, params = {}) => {
8157
8192
  path = path.slice(0, -1);
8158
8193
  }
8159
8194
 
8195
+ // Check if we'll have query parameters to decide on trailing slash removal
8196
+ const willHaveQueryParams =
8197
+ parsedPattern.queryParams?.some((qp) => {
8198
+ const value = params[qp.name];
8199
+ return value !== undefined;
8200
+ }) ||
8201
+ Object.entries(params).some(([key, value]) => {
8202
+ const isPathParam = parsedPattern.segments.some(
8203
+ (s) => s.type === "param" && s.name === key,
8204
+ );
8205
+ const isQueryParam = parsedPattern.queryParams?.some(
8206
+ (qp) => qp.name === key,
8207
+ );
8208
+ return value !== undefined && !isPathParam && !isQueryParam;
8209
+ });
8210
+
8211
+ // Remove trailing slash when we have query params for prettier URLs
8212
+ if (willHaveQueryParams && path.endsWith("/") && path !== "/") {
8213
+ path = path.slice(0, -1);
8214
+ }
8215
+
8160
8216
  // Add search parameters
8161
8217
  const pathParamNames = new Set(
8162
8218
  parsedPattern.segments.filter((s) => s.type === "param").map((s) => s.name),
@@ -8255,6 +8311,43 @@ const isChildPattern = (childPattern, parentPattern) => {
8255
8311
  return childSegments.length > parentSegments.length || hasMoreSpecificSegment;
8256
8312
  };
8257
8313
 
8314
+ /**
8315
+ * Check if a parameter is redundant because the child pattern already has it as a literal segment
8316
+ * E.g., parameter "section" is redundant for pattern "/admin/settings/:tab" because "settings" is literal
8317
+ */
8318
+ const isParameterRedundantWithLiteralSegments = (
8319
+ childPattern,
8320
+ parentPattern,
8321
+ paramName,
8322
+ ) => {
8323
+ // Find which segment position corresponds to this parameter in the parent
8324
+ let paramSegmentIndex = -1;
8325
+ for (let i = 0; i < parentPattern.segments.length; i++) {
8326
+ const segment = parentPattern.segments[i];
8327
+ if (segment.type === "param" && segment.name === paramName) {
8328
+ paramSegmentIndex = i;
8329
+ break;
8330
+ }
8331
+ }
8332
+
8333
+ // If parameter not found in parent segments, it's not redundant with path
8334
+ if (paramSegmentIndex === -1) {
8335
+ return false;
8336
+ }
8337
+
8338
+ // Check if child has a literal segment at the same position
8339
+ if (childPattern.segments.length > paramSegmentIndex) {
8340
+ const childSegment = childPattern.segments[paramSegmentIndex];
8341
+ if (childSegment.type === "literal") {
8342
+ // Child has a literal segment where parent has parameter
8343
+ // This means the child is more specific and shouldn't inherit this parameter
8344
+ return true; // Redundant - child already specifies this position with a literal
8345
+ }
8346
+ }
8347
+
8348
+ return false;
8349
+ };
8350
+
8258
8351
  /**
8259
8352
  * Register all patterns at once and build their relationships
8260
8353
  */
@@ -8626,6 +8719,13 @@ const registerRoute = (routePattern) => {
8626
8719
  for (const { signal: stateSignal, paramName, options = {} } of connections) {
8627
8720
  const { debug } = options;
8628
8721
 
8722
+ if (debug) {
8723
+ console.debug(
8724
+ `[route] connecting param "${paramName}" to signal`,
8725
+ stateSignal,
8726
+ );
8727
+ }
8728
+
8629
8729
  // URL -> Signal synchronization
8630
8730
  effect(() => {
8631
8731
  const matching = matchingSignal.value;