@jsenv/navi 0.16.26 → 0.16.27

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.
@@ -2664,6 +2664,7 @@ const stateSignal = (defaultValue, options = {}) => {
2664
2664
  getDefaultValue,
2665
2665
  defaultValue: staticDefaultValue,
2666
2666
  dynamicDefaultSignal,
2667
+ isCustomValue,
2667
2668
  type,
2668
2669
  persists,
2669
2670
  localStorageKey,
@@ -7847,21 +7848,26 @@ const createRoutePattern = (pattern) => {
7847
7848
 
7848
7849
  /**
7849
7850
  * Helper: Filter out default values from parameters for cleaner URLs
7851
+ *
7852
+ * This function removes parameters that match their default values (static or dynamic)
7853
+ * while preserving custom values and inherited parameters from ancestor routes.
7854
+ * Parameter inheritance from parent routes is intentional - only default values
7855
+ * for the current route's own parameters are filtered out.
7850
7856
  */
7851
7857
  const removeDefaultValues = (params) => {
7852
7858
  const filtered = { ...params };
7853
7859
 
7854
7860
  for (const connection of connections) {
7855
- const { paramName, signal } = connection;
7856
- const defaultValue = parameterDefaults.get(paramName);
7861
+ const { paramName, signal, options } = connection;
7857
7862
 
7858
- if (paramName in filtered && filtered[paramName] === defaultValue) {
7859
- delete filtered[paramName];
7860
- } else if (
7861
- !(paramName in filtered) &&
7862
- signal?.value !== undefined &&
7863
- signal.value !== defaultValue
7864
- ) {
7863
+ if (paramName in filtered) {
7864
+ // Parameter is explicitly provided - check if we should remove it
7865
+ if (!options.isCustomValue?.(filtered[paramName])) {
7866
+ // Parameter value is not custom (matches default) - remove it
7867
+ delete filtered[paramName];
7868
+ }
7869
+ } else if (options.isCustomValue?.(signal.value)) {
7870
+ // Parameter not provided but signal has custom value - add it
7865
7871
  filtered[paramName] = signal.value;
7866
7872
  }
7867
7873
  }
@@ -7880,7 +7886,7 @@ const createRoutePattern = (pattern) => {
7880
7886
  const effectiveValue = userValue !== undefined ? userValue : signalValue;
7881
7887
  return (
7882
7888
  effectiveValue === literalValue &&
7883
- effectiveValue !== conn.options.defaultValue
7889
+ conn.options.isCustomValue?.(effectiveValue)
7884
7890
  );
7885
7891
  });
7886
7892
 
@@ -7918,7 +7924,7 @@ const createRoutePattern = (pattern) => {
7918
7924
  const signalValue = conn.signal?.value;
7919
7925
  return (
7920
7926
  signalValue === literalValue &&
7921
- signalValue !== conn.options.defaultValue
7927
+ conn.options.isCustomValue?.(signalValue)
7922
7928
  );
7923
7929
  });
7924
7930
 
@@ -8061,10 +8067,10 @@ const createRoutePattern = (pattern) => {
8061
8067
  } else {
8062
8068
  const { paramName: name, signal, options } = item;
8063
8069
  paramName = name;
8064
- // Only include non-default parent signal values
8070
+ // Only include custom parent signal values (not using defaults)
8065
8071
  if (
8066
8072
  signal?.value === undefined ||
8067
- signal.value === options.defaultValue
8073
+ !options.isCustomValue?.(signal.value)
8068
8074
  ) {
8069
8075
  return { isCompatible: true, shouldInclude: false };
8070
8076
  }
@@ -8214,7 +8220,6 @@ const createRoutePattern = (pattern) => {
8214
8220
 
8215
8221
  for (const connection of childPatternObj.connections) {
8216
8222
  const { paramName, signal, options } = connection;
8217
- const defaultValue = options.defaultValue;
8218
8223
 
8219
8224
  // Check if parameter was explicitly provided by user
8220
8225
  const hasExplicitParam = paramName in params;
@@ -8223,13 +8228,16 @@ const createRoutePattern = (pattern) => {
8223
8228
  if (hasExplicitParam) {
8224
8229
  // User explicitly provided this parameter - use their value
8225
8230
  childParams[paramName] = explicitValue;
8226
- if (explicitValue !== undefined && explicitValue !== defaultValue) {
8231
+ if (
8232
+ explicitValue !== undefined &&
8233
+ options.isCustomValue?.(explicitValue)
8234
+ ) {
8227
8235
  hasActiveParams = true;
8228
8236
  }
8229
8237
  } else if (signal?.value !== undefined) {
8230
8238
  // No explicit override - use signal value
8231
8239
  childParams[paramName] = signal.value;
8232
- if (signal.value !== defaultValue) {
8240
+ if (options.isCustomValue?.(signal.value)) {
8233
8241
  hasActiveParams = true;
8234
8242
  }
8235
8243
  }
@@ -8309,7 +8317,7 @@ const createRoutePattern = (pattern) => {
8309
8317
  // Check if parameters that determine child selection are non-default
8310
8318
  // OR if any descendant parameters indicate explicit navigation
8311
8319
  for (const connection of connections) {
8312
- const { paramName } = connection;
8320
+ const { paramName, options } = connection;
8313
8321
  const defaultValue = parameterDefaults.get(paramName);
8314
8322
  const resolvedValue = resolvedParams[paramName];
8315
8323
  const userProvidedParam = paramName in params;
@@ -8318,9 +8326,10 @@ const createRoutePattern = (pattern) => {
8318
8326
  // This literal corresponds to a parameter in the parent
8319
8327
  if (
8320
8328
  userProvidedParam ||
8321
- (resolvedValue !== undefined && resolvedValue !== defaultValue)
8329
+ (resolvedValue !== undefined &&
8330
+ options.isCustomValue?.(resolvedValue))
8322
8331
  ) {
8323
- // Parameter was explicitly provided or has non-default value - child is needed
8332
+ // Parameter was explicitly provided or has custom value - child is needed
8324
8333
  childSpecificParamsAreDefaults = false;
8325
8334
  break;
8326
8335
  }
@@ -8421,7 +8430,7 @@ const createRoutePattern = (pattern) => {
8421
8430
  // If explicitly undefined, don't include it (which means don't use child route)
8422
8431
  } else if (
8423
8432
  signal?.value !== undefined &&
8424
- signal.value !== options.defaultValue
8433
+ options.isCustomValue?.(signal.value)
8425
8434
  ) {
8426
8435
  // No explicit override - use signal value if non-default
8427
8436
  baseParams[paramName] = signal.value;
@@ -8439,7 +8448,6 @@ const createRoutePattern = (pattern) => {
8439
8448
  // Add parent's signal parameters
8440
8449
  for (const connection of parentPatternObj.connections) {
8441
8450
  const { paramName, signal, options } = connection;
8442
- const defaultValue = options.defaultValue;
8443
8451
 
8444
8452
  // Skip if child route already handles this parameter
8445
8453
  const childConnection = childPatternObj.connections.find(
@@ -8454,8 +8462,11 @@ const createRoutePattern = (pattern) => {
8454
8462
  continue; // Already have this parameter
8455
8463
  }
8456
8464
 
8457
- // Only include non-default signal values
8458
- if (signal?.value !== undefined && signal.value !== defaultValue) {
8465
+ // Only include custom signal values (not using defaults)
8466
+ if (
8467
+ signal?.value !== undefined &&
8468
+ options.isCustomValue?.(signal.value)
8469
+ ) {
8459
8470
  // Skip if parameter is consumed by child's literal path segments
8460
8471
  const isConsumedByChildPath = childPatternObj.pattern.segments.some(
8461
8472
  (segment) =>
@@ -8521,10 +8532,9 @@ const createRoutePattern = (pattern) => {
8521
8532
 
8522
8533
  if (childConnection) {
8523
8534
  const { options } = childConnection;
8524
- const defaultValue = options.defaultValue;
8525
8535
 
8526
- // Only include if it's NOT the signal's default value
8527
- if (userValue !== defaultValue) {
8536
+ // Only include if it's a custom value (not default)
8537
+ if (options.isCustomValue?.(userValue)) {
8528
8538
  baseParams[paramName] = userValue;
8529
8539
  } else {
8530
8540
  // User provided the default value - complete omission
@@ -8584,7 +8594,7 @@ const createRoutePattern = (pattern) => {
8584
8594
  const hasNonDefaultChildParams = (childPatternObj.connections || []).some(
8585
8595
  (childConnection) => {
8586
8596
  const { signal, options } = childConnection;
8587
- return signal?.value !== options.defaultValue;
8597
+ return options.isCustomValue?.(signal?.value);
8588
8598
  },
8589
8599
  );
8590
8600
 
@@ -8794,13 +8804,15 @@ const createRoutePattern = (pattern) => {
8794
8804
  // 2. The source route has only query parameters that are non-default
8795
8805
  const hasNonDefaultPathParams = connections.some((connection) => {
8796
8806
  const resolvedValue = resolvedParams[connection.paramName];
8797
- const defaultValue = connection.options.defaultValue;
8807
+
8798
8808
  // Check if this is a query parameter (not in the pattern path)
8799
8809
  const isQueryParam = parsedPattern.queryParams.some(
8800
8810
  (qp) => qp.name === connection.paramName,
8801
8811
  );
8802
8812
  // Allow non-default query parameters, but not path parameters
8803
- return !isQueryParam && resolvedValue !== defaultValue;
8813
+ return (
8814
+ !isQueryParam && connection.options.isCustomValue?.(resolvedValue)
8815
+ );
8804
8816
  });
8805
8817
 
8806
8818
  if (hasNonDefaultPathParams) {
@@ -8830,8 +8842,7 @@ const createRoutePattern = (pattern) => {
8830
8842
  // For non-immediate parents, only allow optimization if all resolved parameters have default values
8831
8843
  const hasNonDefaultParameters = connections.some((connection) => {
8832
8844
  const resolvedValue = resolvedParams[connection.paramName];
8833
- const defaultValue = connection.options.defaultValue;
8834
- return resolvedValue !== defaultValue;
8845
+ return connection.options.isCustomValue?.(resolvedValue);
8835
8846
  });
8836
8847
 
8837
8848
  if (hasNonDefaultParameters) {
@@ -9100,13 +9111,12 @@ const createRoutePattern = (pattern) => {
9100
9111
  // Also check target ancestor's own signal values for parameters not in resolvedParams
9101
9112
  for (const connection of targetAncestor.connections) {
9102
9113
  const { paramName, signal, options } = connection;
9103
- const defaultValue = options.defaultValue;
9104
9114
 
9105
- // Only include if not already processed and has non-default value
9115
+ // Only include if not already processed and has custom value (not default)
9106
9116
  if (
9107
9117
  !(paramName in ancestorParams) &&
9108
9118
  signal?.value !== undefined &&
9109
- signal.value !== defaultValue
9119
+ options.isCustomValue?.(signal.value)
9110
9120
  ) {
9111
9121
  // Don't include path parameters that correspond to literal segments we're optimizing away
9112
9122
  const targetParam = targetParams.find((p) => p.name === paramName);
@@ -9136,13 +9146,12 @@ const createRoutePattern = (pattern) => {
9136
9146
  while (currentParent) {
9137
9147
  for (const connection of currentParent.connections) {
9138
9148
  const { paramName, signal, options } = connection;
9139
- const defaultValue = options.defaultValue;
9140
9149
 
9141
- // Only inherit non-default values that we don't already have
9150
+ // Only inherit custom values (not defaults) that we don't already have
9142
9151
  if (
9143
9152
  !(paramName in ancestorParams) &&
9144
9153
  signal?.value !== undefined &&
9145
- signal.value !== defaultValue
9154
+ options.isCustomValue?.(signal.value)
9146
9155
  ) {
9147
9156
  // Check if this parameter would be redundant with target ancestor's literal segments
9148
9157
  const isRedundant = isParameterRedundantWithLiteralSegments(
@@ -9217,13 +9226,12 @@ const createRoutePattern = (pattern) => {
9217
9226
  // Check parent's signal connections for non-default values to inherit
9218
9227
  for (const parentConnection of currentParent.connections) {
9219
9228
  const { paramName, signal, options } = parentConnection;
9220
- const defaultValue = options.defaultValue;
9221
9229
 
9222
- // Only inherit if we don't have this param and parent has non-default value
9230
+ // Only inherit if we don't have this param and parent has custom value (not default)
9223
9231
  if (
9224
9232
  !(paramName in finalParams) &&
9225
9233
  signal?.value !== undefined &&
9226
- signal.value !== defaultValue
9234
+ options.isCustomValue?.(signal.value)
9227
9235
  ) {
9228
9236
  // Don't inherit if parameter corresponds to a literal in our path
9229
9237
  const shouldInherit = !isParameterRedundantWithLiteralSegments(
@@ -9707,6 +9715,21 @@ const extractSearchParams = (urlObj, connections = []) => {
9707
9715
  /**
9708
9716
  * Build query parameters respecting hierarchical order from ancestor patterns
9709
9717
  */
9718
+ /**
9719
+ * Build hierarchical query parameters from pattern hierarchy
9720
+ *
9721
+ * IMPORTANT: This function implements parameter inheritance - child routes inherit
9722
+ * query parameters from their ancestor routes. This is intentional behavior that
9723
+ * allows child routes to preserve context from parent routes.
9724
+ *
9725
+ * For example:
9726
+ * - Parent route: /map/?lon=123
9727
+ * - Child route: /map/isochrone?iso_lon=456
9728
+ * - Final URL: /map/isochrone?lon=123&iso_lon=456
9729
+ *
9730
+ * The child route inherits 'lon' from its parent, maintaining navigation context.
9731
+ * Only parameters that match their defaults (static or dynamic) are omitted.
9732
+ */
9710
9733
  const buildHierarchicalQueryParams = (
9711
9734
  parsedPattern,
9712
9735
  params,