@jsenv/navi 0.16.1 → 0.16.3

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.
@@ -7528,15 +7528,24 @@ const detectSignals = (routePattern) => {
7528
7528
  const signalConnections = [];
7529
7529
  let updatedPattern = routePattern;
7530
7530
 
7531
- // Look for signals in the new syntax: :paramName={navi_state_signal:id} or ?paramName={navi_state_signal:id} or &paramName={navi_state_signal:id}
7532
- // Using curly braces to avoid conflicts with underscores in signal IDs
7533
- const signalParamRegex = /([?:&])(\w+)=(\{navi_state_signal:[^}]+\})/g;
7531
+ // Look for signals in two formats:
7532
+ // 1. Expected format: :paramName={navi_state_signal:id} or ?paramName={navi_state_signal:id} or &paramName={navi_state_signal:id}
7533
+ // 2. Typoe format (missing = sign): &paramName{navi_state_signal:id}
7534
+ const signalParamRegex = /([?:&])(\w+)(=)?(\{navi_state_signal:[^}]+\})/g;
7534
7535
  let match;
7535
7536
 
7536
7537
  while ((match = signalParamRegex.exec(routePattern)) !== null) {
7537
- const [fullMatch, prefix, paramName, signalString] = match;
7538
+ const [fullMatch, prefix, paramName, equalSign, signalString] = match;
7538
7539
 
7539
- // Extract the signal ID from the new format: {navi_state_signal:id}
7540
+ // Emit warning if equal sign is missing
7541
+ if (!equalSign) {
7542
+ console.warn(
7543
+ `[detectSignals] Missing '=' sign in route pattern: "${prefix}${paramName}${signalString}". ` +
7544
+ `Consider using "${prefix}${paramName}=${signalString}" for better clarity.`,
7545
+ );
7546
+ }
7547
+
7548
+ // Extract the signal ID from the format: {navi_state_signal:id}
7540
7549
  const signalIdMatch = signalString.match(/\{navi_state_signal:([^}]+)\}/);
7541
7550
  if (!signalIdMatch) {
7542
7551
  console.warn(
@@ -7553,13 +7562,10 @@ const detectSignals = (routePattern) => {
7553
7562
 
7554
7563
  let replacement;
7555
7564
  if (prefix === ":") {
7556
- // Path parameter: :section=__jsenv_signal_1__ becomes :section
7565
+ // Path parameter: :section={navi_state_signal:...} becomes :section
7557
7566
  replacement = `${prefix}${paramName}`;
7558
- } else if (prefix === "?") {
7559
- // First search parameter: ?city=__jsenv_signal_1__ becomes ?city
7560
- replacement = `${prefix}${paramName}`;
7561
- } else if (prefix === "&") {
7562
- // Additional search parameter: &lon=__jsenv_signal_1__ becomes &lon
7567
+ } else if (prefix === "?" || prefix === "&") {
7568
+ // Query parameter: ?city={navi_state_signal:...} or &lon{navi_state_signal:...} becomes ?city or &lon
7563
7569
  replacement = `${prefix}${paramName}`;
7564
7570
  }
7565
7571
  updatedPattern = updatedPattern.replace(fullMatch, replacement);
@@ -7606,6 +7612,7 @@ const createRoutePattern = (pattern) => {
7606
7612
  const result = matchUrl(parsedPattern, url, {
7607
7613
  parameterDefaults,
7608
7614
  baseUrl,
7615
+ connections,
7609
7616
  });
7610
7617
 
7611
7618
  return result;
@@ -7985,7 +7992,11 @@ const checkIfLiteralCanBeOptional = (literalValue, patternRegistry) => {
7985
7992
  /**
7986
7993
  * Match a URL against a parsed pattern
7987
7994
  */
7988
- const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
7995
+ const matchUrl = (
7996
+ parsedPattern,
7997
+ url,
7998
+ { parameterDefaults, baseUrl, connections = [] },
7999
+ ) => {
7989
8000
  // Parse the URL
7990
8001
  const urlObj = new URL(url, baseUrl);
7991
8002
  let pathname = urlObj.pathname;
@@ -8007,14 +8018,14 @@ const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
8007
8018
  // OR when URL exactly matches baseUrl (treating baseUrl as root)
8008
8019
  if (parsedPattern.segments.length === 0) {
8009
8020
  if (pathname === "/" || pathname === "") {
8010
- return extractSearchParams(urlObj);
8021
+ return extractSearchParams(urlObj, connections);
8011
8022
  }
8012
8023
 
8013
8024
  // Special case: if URL exactly matches baseUrl, treat as root route
8014
8025
  if (baseUrl) {
8015
8026
  const baseUrlObj = new URL(baseUrl);
8016
8027
  if (originalPathname === baseUrlObj.pathname) {
8017
- return extractSearchParams(urlObj);
8028
+ return extractSearchParams(urlObj, connections);
8018
8029
  }
8019
8030
  }
8020
8031
 
@@ -8109,7 +8120,7 @@ const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
8109
8120
  // If pattern has trailing slash or wildcard, allow extra segments (no additional check needed)
8110
8121
 
8111
8122
  // Add search parameters
8112
- const searchParams = extractSearchParams(urlObj);
8123
+ const searchParams = extractSearchParams(urlObj, connections);
8113
8124
  Object.assign(params, searchParams);
8114
8125
 
8115
8126
  // Apply remaining parameter defaults for unmatched parameters
@@ -8125,10 +8136,29 @@ const matchUrl = (parsedPattern, url, { parameterDefaults, baseUrl }) => {
8125
8136
  /**
8126
8137
  * Extract search parameters from URL
8127
8138
  */
8128
- const extractSearchParams = (urlObj) => {
8139
+ const extractSearchParams = (urlObj, connections = []) => {
8129
8140
  const params = {};
8141
+
8142
+ // Create a map for quick signal type lookup
8143
+ const signalTypes = new Map();
8144
+ for (const connection of connections) {
8145
+ if (connection.options.type) {
8146
+ signalTypes.set(connection.paramName, connection.options.type);
8147
+ }
8148
+ }
8149
+
8130
8150
  for (const [key, value] of urlObj.searchParams) {
8131
- params[key] = value;
8151
+ const signalType = signalTypes.get(key);
8152
+
8153
+ // Cast value based on signal type
8154
+ if (signalType === "number" || signalType === "float") {
8155
+ const numberValue = Number(value);
8156
+ params[key] = isNaN(numberValue) ? value : numberValue;
8157
+ } else if (signalType === "boolean") {
8158
+ params[key] = value === "true" || value === "1";
8159
+ } else {
8160
+ params[key] = value;
8161
+ }
8132
8162
  }
8133
8163
  return params;
8134
8164
  };
@@ -8784,12 +8814,48 @@ const registerRoute = (routePattern) => {
8784
8814
  );
8785
8815
  return null;
8786
8816
  }
8787
- if (route.action) {
8788
- // For action: merge with resolved params (includes defaults) so action gets complete params
8789
- const currentResolvedParams = routePattern.resolveParams();
8790
- const updatedActionParams = { ...currentResolvedParams, ...newParams };
8791
- route.action.replaceParams(updatedActionParams);
8817
+
8818
+ // Find all matching routes and update their actions, then delegate to most specific
8819
+ const allMatchingRoutes = Array.from(routeSet).filter((r) => r.matching);
8820
+
8821
+ // Update action params on all matching routes
8822
+ for (const matchingRoute of allMatchingRoutes) {
8823
+ if (matchingRoute.action) {
8824
+ const matchingRoutePrivateProperties =
8825
+ getRoutePrivateProperties(matchingRoute);
8826
+ if (matchingRoutePrivateProperties) {
8827
+ const { routePattern: matchingRoutePattern } =
8828
+ matchingRoutePrivateProperties;
8829
+ const currentResolvedParams = matchingRoutePattern.resolveParams();
8830
+ const updatedActionParams = {
8831
+ ...currentResolvedParams,
8832
+ ...newParams,
8833
+ };
8834
+ matchingRoute.action.replaceParams(updatedActionParams);
8835
+ }
8836
+ }
8792
8837
  }
8838
+
8839
+ // Find the most specific route (the one with the longest pattern path)
8840
+ let mostSpecificRoute = route;
8841
+ let maxSegments = route.pattern.split("/").filter((s) => s !== "").length;
8842
+
8843
+ for (const matchingRoute of allMatchingRoutes) {
8844
+ const segments = matchingRoute.pattern
8845
+ .split("/")
8846
+ .filter((s) => s !== "").length;
8847
+ if (segments > maxSegments) {
8848
+ maxSegments = segments;
8849
+ mostSpecificRoute = matchingRoute;
8850
+ }
8851
+ }
8852
+
8853
+ // If we found a more specific route, delegate to it; otherwise handle it ourselves
8854
+ if (mostSpecificRoute !== route) {
8855
+ return mostSpecificRoute.redirectTo(newParams);
8856
+ }
8857
+
8858
+ // This route is the most specific, handle the redirect ourselves
8793
8859
  return route.redirectTo(newParams);
8794
8860
  };
8795
8861
  route.buildRelativeUrl = (params) => {