Repository: tighten/ziggy (js)

Issue:
Percent sign in URL results in a URI malformed error when using route().current()
### Ziggy version

2.3.0

### Laravel version

11.28.0

### Description

My website has profile section where you can view users usernames, the structure is site.com/user/{username}

We have a requirement to allow users to use the % sign in their username. I'm making sure to urlencode the username to prevent issues on the server side. 

So for example a URL might be site.com/user/john%25

The issue comes when I call route().current('user.profile'), I get the following error in my browser: `URIError: URI malformed`

![image](https://github.com/user-attachments/assets/758d7b87-df13-4b48-a649-4e00b1f80c53)



### Ziggy call and context

```js
route().current('user.profile')
```


### Ziggy configuration

```js
Can provide if needed.
```


### Route definition

```php
Route::get('/user/{username}', [PageController::class, 'userProfile'])->name('user.profile');
```


Pull request:
Fix params sometimes being double-decoded
Calling `decodeURI` before we try to match params allows us to handle Cyrillic characters, but it decodes other characters too, which means `decodeURIComponent` a few lines later tries to decode them again. I think this actually doesn't matter for most characters, but `%` specifically causes problems because it can't be decoded again on its own.

I'm not positive that this solution works perfectly, if it causes other issues I might tweak how we support Cyrillic characters. Cyrillic characters are only decoded for _display_, in `location.href` and other JavaScript stuff they're kept encoded so we might need to do that too.

Fixes #777.

Patch:
diff --git a/src/js/Route.js b/src/js/Route.js
index eb8084c..d0a9e8d 100644
--- a/src/js/Route.js
+++ b/src/js/Route.js
@@ -91,7 +91,9 @@ export default class Route {
 
         const [location, query] = url.replace(/^\w+:\/\//, '').split('?');
 
-        const matches = new RegExp(`^${pattern}/?$`).exec(decodeURI(location));
+        const matches =
+            new RegExp(`^${pattern}/?$`).exec(location) ??
+            new RegExp(`^${pattern}/?$`).exec(decodeURI(location));
 
         if (matches) {
             for (const k in matches.groups) {


Test patch:
diff --git a/tests/js/route.test.js b/tests/js/route.test.js
index e530280..cd2b28f 100644
--- a/tests/js/route.test.js
+++ b/tests/js/route.test.js
@@ -1412,6 +1412,13 @@ describe('current()', () => {
         expect(route().current('statistics')).toBe(true);
     });
 
+    test('can check the current route with encoded percent sign', () => {
+        global.window.location.pathname = '/optionalpage/john%25';
+
+        expect(route().current()).toBe('pages.optional');
+        expect(route().current('pages.optional')).toBe(true);
+    });
+
     test('can ignore routes that don’t allow GET requests', () => {
         global.window.location.pathname = '/posts/1';