@@ -489,50 +489,12 @@ export function findLast<T>(
489489 return undefined
490490}
491491
492- const DECODE_IGNORE_LIST = [
493- '%25' , // %
494- '%5C' , // \
495- ]
496-
497- function splitAndDecode (
498- part : string ,
499- decodeIgnore : Array < string > ,
500- startIndex = 0 ,
501- ) : string {
502- // decode the path / path segment by splitting it into parts defined by the ignore list.
503- // once these pieces have been decoded, join them back together to form the final decoded path segment with the ignored character in place.
504- // we walk through the ignore list linearly, breaking the segment up into pieces and decoding each piece individually.
505- // use index traversal to avoid making unnecessary copies of the array.
506- for ( let i = startIndex ; i < decodeIgnore . length ; i ++ ) {
507- const char = decodeIgnore [ i ] ! . toUpperCase ( )
508-
509- // check if the part includes the current ignore character
510- // if it doesn't continue to the next ignore character
511- if ( part . includes ( char ) ) {
512- // split the part into pieces that needs to be checked and decoded
513- const partsToDecode = part . split ( char )
514- const partsToJoin = new Array < string > ( partsToDecode . length )
515-
516- // now check and decode each piece individually taking into consideration the remaining ignored characters.
517- // since we are walking through the list linearly, we only need to consider ignore items not yet traversed.
518- for ( let j = 0 ; j < partsToDecode . length ; j ++ ) {
519- const partToDecode = partsToDecode [ j ] !
520- // once we have traversed the entire ignore list, each decoded part is returned.
521- partsToJoin [ j ] = splitAndDecode ( partToDecode , decodeIgnore , i + 1 )
522- }
523-
524- // and join them back together to form the final decoded path segment with the ignored character in place.
525- return partsToJoin . join ( char )
526- }
527- }
528-
529- // once we have reached the end of the ignore list, we start walking back returning each decoded part.
530- // should there be no matching characters, the path segment as a whole will be decoded.
492+ function decodeSegment ( segment : string ) : string {
531493 try {
532- return decodeURI ( part )
494+ return decodeURI ( segment )
533495 } catch {
534496 // if the decoding fails, try to decode the various parts leaving the malformed tags in place
535- return part . replaceAll ( / % [ 0 - 9 A - F ] { 2 } / g , ( match ) => {
497+ return segment . replaceAll ( / % [ 0 - 9 A - F ] { 2 } / gi , ( match ) => {
536498 try {
537499 return decodeURI ( match )
538500 } catch {
@@ -542,17 +504,17 @@ function splitAndDecode(
542504 }
543505}
544506
545- export function decodePath (
546- part : string ,
547- decodeIgnore : Array < string > = DECODE_IGNORE_LIST ,
548- ) : string {
549- // if the path segment does not contain any encoded uri components return the path as is
550- if ( part === '' || ! / % [ 0 - 9 A - F a - f ] { 2 } / g . test ( part ) ) return part
551-
552- // ensure all encoded characters are uppercase
553- const normalizedPart = part . replaceAll ( / % [ 0 - 9 a - f ] { 2 } / g , ( match ) =>
554- match . toUpperCase ( ) ,
555- )
556-
557- return splitAndDecode ( normalizedPart , decodeIgnore )
507+ export function decodePath ( path : string , decodeIgnore ?: Array < string > ) : string {
508+ if ( ! path ) return path
509+ const re = decodeIgnore
510+ ? new RegExp ( ` ${ decodeIgnore . join ( '|' ) } ` , 'gi' )
511+ : / % 2 5 | % 5 C / gi
512+ let cursor = 0
513+ let result = ''
514+ let match
515+ while ( null !== ( match = re . exec ( path ) ) ) {
516+ result += decodeSegment ( path . slice ( cursor , match . index ) ) + match [ 0 ]
517+ cursor = re . lastIndex
518+ }
519+ return result + decodeSegment ( cursor ? path . slice ( cursor ) : path )
558520}
0 commit comments