@@ -519,55 +519,116 @@ function urlMatchesPattern(url, pattern) {
519
519
// Handle edge cases
520
520
if ( pattern === url ) return true ;
521
521
if ( ! url || ! pattern ) return false ;
522
-
522
+
523
+ // Normalize pattern if missing scheme
524
+ if ( ! pattern . includes ( "://" ) ) {
525
+ pattern = "*://" + pattern ;
526
+ }
527
+
523
528
// Extract scheme, host, and path from pattern
524
- let schemeHost , patternPath ;
525
-
526
- if ( pattern . includes ( "://" ) ) {
527
- [ schemeHost , ...patternPathParts ] = pattern . split ( "/" ) ;
528
- patternPath = patternPathParts . length > 0 ? "/" + patternPathParts . join ( "/" ) : "/*" ;
529
-
530
- // Fix handling of the scheme/host part
531
- let [ patternScheme , patternHost ] = schemeHost . split ( "://" ) ;
532
-
533
- // Create URL object for the input URL
534
- const urlObj = new URL ( url ) ;
535
-
536
- // Handle scheme matching
537
- if ( patternScheme !== "*" && patternScheme !== urlObj . protocol . slice ( 0 , - 1 ) ) {
529
+ let [ schemeHostPart , ...patternPathParts ] = pattern . split ( "/" ) ;
530
+ let patternPath =
531
+ patternPathParts . length > 0 ? "/" + patternPathParts . join ( "/" ) : "/" ;
532
+
533
+ // Fix handling of the scheme/host part
534
+ let [ patternScheme , ...hostParts ] = schemeHostPart . split ( "://" ) ;
535
+ let patternHost = hostParts . join ( "://" ) ; // In case there's :// in the hostname (unlikely but safe)
536
+
537
+ // Create URL object for the input URL
538
+ const urlObj = new URL ( url ) ;
539
+ const urlPath = urlObj . pathname ;
540
+
541
+ // Handle scheme matching
542
+ if (
543
+ patternScheme !== "*" &&
544
+ patternScheme !== urlObj . protocol . slice ( 0 , - 1 )
545
+ ) {
546
+ return false ;
547
+ }
548
+
549
+ // Handle host matching with wildcards
550
+ if ( patternHost . startsWith ( "*." ) ) {
551
+ // *.domain.com pattern - match domain or any subdomain
552
+ const domain = patternHost . slice ( 2 ) ;
553
+ if (
554
+ ! ( urlObj . hostname === domain || urlObj . hostname . endsWith ( "." + domain ) )
555
+ ) {
538
556
return false ;
539
557
}
540
-
541
- // Handle host matching with wildcard
542
- if ( patternHost . startsWith ( "*." ) ) {
543
- if ( ! urlObj . hostname . endsWith ( patternHost . slice ( 2 ) ) ) {
544
- return false ;
545
- }
546
- } else if ( patternHost !== "*" && patternHost !== urlObj . hostname ) {
558
+ } else if ( patternHost . includes ( "*" ) ) {
559
+ // Handle other wildcards in hostname
560
+ const hostRegex = new RegExp (
561
+ "^" + patternHost . replace ( / \. / g, "\\." ) . replace ( / \* / g, ".*" ) + "$"
562
+ ) ;
563
+ if ( ! hostRegex . test ( urlObj . hostname ) ) {
547
564
return false ;
548
565
}
549
-
550
- // Handle path matching
551
- if ( patternPath === "/*" ) {
552
- return true ; // Match any path
566
+ } else if ( patternHost !== "*" && patternHost !== urlObj . hostname ) {
567
+ // Direct hostname match
568
+ return false ;
569
+ }
570
+
571
+ // If pattern path is empty or just "/" or "/*", match any path
572
+ if ( patternPath === "/" || patternPath === "/*" ) {
573
+ return true ;
574
+ }
575
+
576
+ // Handle special case for /** at the end
577
+ if ( patternPath . endsWith ( "/**" ) ) {
578
+ const basePath = patternPath . slice ( 0 , - 3 ) ;
579
+ return urlPath === basePath || urlPath . startsWith ( basePath ) ;
580
+ }
581
+
582
+ // Handle path matching with both * and ** wildcards
583
+ const pathSegments = patternPath
584
+ . split ( "/" )
585
+ . filter ( ( segment ) => segment !== "" ) ;
586
+ const urlSegments = urlPath . split ( "/" ) . filter ( ( segment ) => segment !== "" ) ;
587
+
588
+ // Simple case: if pattern is just /* match any single-level path
589
+ if ( pathSegments . length === 1 && pathSegments [ 0 ] === "*" ) {
590
+ return true ;
591
+ }
592
+
593
+ let pathRegexParts = [ "^" ] ;
594
+ let i = 0 ;
595
+
596
+ for ( i = 0 ; i < pathSegments . length ; i ++ ) {
597
+ const segment = pathSegments [ i ] ;
598
+
599
+ // Handle ** wildcard (matches across multiple path segments)
600
+ if ( segment === "**" ) {
601
+ // If this is the last segment, match anything that follows
602
+ if ( i === pathSegments . length - 1 ) {
603
+ pathRegexParts . push ( ".*" ) ;
604
+ break ;
605
+ }
606
+
607
+ // Otherwise, match anything until we find the next segment
608
+ const nextSegment = pathSegments [ i + 1 ] ;
609
+ const nextSegmentRegex = nextSegment
610
+ . replace ( / \* / g, "[^/]*" )
611
+ . replace ( / \. / g, "\\." ) ;
612
+
613
+ pathRegexParts . push ( `(?:.*?\\/)?${ nextSegmentRegex } ` ) ;
614
+ i ++ ; // Skip the next segment as we've already included it
553
615
} else {
554
- // Convert pattern path to regex pattern
555
- const pathPattern = patternPath
556
- . split ( "/" )
557
- . map ( segment => {
558
- if ( segment === "*" ) return "[^/]*" ;
559
- if ( segment . includes ( "*" ) ) return segment . replace ( / \* / g, "[^/]*" ) ;
560
- return segment ;
561
- } )
562
- . join ( "/" ) ;
563
-
564
- const pathRegex = new RegExp ( `^${ pathPattern } $` ) ;
565
- return pathRegex . test ( urlObj . pathname ) ;
616
+ // Handle regular segment with potential * wildcards
617
+ const segmentRegex = segment
618
+ . replace ( / \* / g, "[^/]*" )
619
+ . replace ( / \. / g, "\\." ) ;
620
+ if ( i === 0 ) {
621
+ pathRegexParts . push ( `\\/?${ segmentRegex } ` ) ; // Make the first slash optional
622
+ } else {
623
+ pathRegexParts . push ( `\\/${ segmentRegex } ` ) ;
624
+ }
566
625
}
567
- } else {
568
- // Handle patterns without scheme
569
- return url . includes ( pattern ) ;
570
626
}
627
+
628
+ pathRegexParts . push ( "$" ) ;
629
+ const pathRegex = new RegExp ( pathRegexParts . join ( "" ) ) ;
630
+
631
+ return pathRegex . test ( urlPath ) ;
571
632
} catch ( error ) {
572
633
console . warn ( "URL matching error:" , error ) ;
573
634
return false ;
0 commit comments