Repository: honojs/hono (TypeScript)

Issue:
Support get real route path in middleware
### What is the feature you are proposing?

Now
```ts
app.use(async (c, next) => {
    console.log(c.req.method, c.req.routePath, c.req.path, c.req.url)
    //             GET           /*            /user/1      http://127.0.0.1:3000/user/1

}

app.get("/user/:id", ...)

// and curl
// curl http://127.0.0.1:3000/user/1
```
Want to
```ts
    console.log(c.req.routePath)
    //          /user/:id
```


Discussion: Hi @seepine 

Now, you can use Route helper: https://hono.dev/docs/helpers/route

```ts
import { Hono } from 'hono'
import { routePath } from 'hono/route'

const app = new Hono()

app.get('/user/:id', (c) => {
  console.log(routePath(c)) // `/user/:id`
  return c.json({})
})

export default app
```
@yusukebe Hi, i want to get real routePath in middleware, not the route handler


```sh
curl http://127.0.0.1:3000/user/1
```

```ts
const app = new Hono()

app.use(async (c, next) => {
    console.log(routePath(c))  // `/*`
    console.log(c.req.path)    // `/user/1`
    await next()
})

app.get('/user/:id', (c) => {
  return c.json({})
})
```

export default app
@seepine 

I see. However, you can't retrieve `/user/:id` in the middleware using that syntax because the middleware knows only the path that it matched.

If you put the middleware with a path like the following, it displays `/user/:id`.

```ts
app.get(
  '/user/:id',
  async (c, next) => {
    console.log(routePath(c)) // /user/:id
    await next()
  },
  (c) => {
    return c.json({})
  }
)
```
Well we can add original path in context itself while creating context .
Isn't?

when we do const machedResults = this.router.match()
const c = new Context ()

Here we just need to make sure router returns path+handlers both and give original path in context 
In case it can help someone as I don't remember this was documented anywhere, calling `getRoutePath` after `next()` will give you the proper route:

```ts
const before =  getRoutePath(c);
await next();
const after = getRoutePath(c);
```
Hi @ex0ns, thanks for the comment.

> https://github.com/honojs/hono/issues/4196#issuecomment-3317581222

Ah, that might need to be fixed as a bug. I think we need a more formal way to get it.
> Hi @ex0ns, thanks for the comment.
> 
> > https://github.com/honojs/hono/issues/4196#issuecomment-3317581222
> 
> Ah, that might need to be fixed as a bug. I think we need a more formal way to get it.

We already send matchedResults in context while creating, The easiest way is to create a method in context class and there we have matchedResults access, we just need to traverse last arrays and get the path, isn't?



Fix (patch):
diff --git a/src/helper/route/index.ts b/src/helper/route/index.ts
index bffebe28f..1587f630e 100644
--- a/src/helper/route/index.ts
+++ b/src/helper/route/index.ts
@@ -37,23 +37,32 @@ export const matchedRoutes = (c: Context): RouterRoute[] =>
  * Get the route path registered within the handler
  *
  * @param {Context} c - The context object
+ * @param {number} index - The index of the root from which to retrieve the path, similar to Array.prototype.at(), where a negative number is the index counted from the end of the matching root. Defaults to the current root index.
  * @returns The route path registered within the handler
  *
  * @example
  * ```ts
  * import { routePath } from 'hono/route'
  *
+ * app.use('*', (c, next) => {
+ *   console.log(routePath(c)) // '*'
+ *   console.log(routePath(c, -1)) // '/posts/:id'
+ *   return next()
+ * })
+ *
  * app.get('/posts/:id', (c) => {
  *   return c.text(routePath(c)) // '/posts/:id'
  * })
  * ```
  */
-export const routePath = (c: Context): string => matchedRoutes(c)[c.req.routeIndex].path
+export const routePath = (c: Context, index?: number): string =>
+  matchedRoutes(c).at(index ?? c.req.routeIndex)?.path ?? ''
 
 /**
  * Get the basePath of the as-is route specified by routing.
  *
  * @param {Context} c - The context object
+ * @param {number} index - The index of the root from which to retrieve the path, similar to Array.prototype.at(), where a negative number is the index counted from the end of the matching root. Defaults to the current root index.
  * @returns The basePath of the as-is route specified by routing.
  *
  * @example
@@ -70,12 +79,14 @@ export const routePath = (c: Context): string => matchedRoutes(c)[c.req.routeInd
  * app.route('/:sub', subApp)
  * ```
  */
-export const baseRoutePath = (c: Context): string => matchedRoutes(c)[c.req.routeIndex].basePath
+export const baseRoutePath = (c: Context, index?: number): string =>
+  matchedRoutes(c).at(index ?? c.req.routeIndex)?.basePath ?? ''
 
 /**
  * Get the basePath with embedded parameters
  *
  * @param {Context} c - The context object
+ * @param {number} index - The index of the root from which to retrieve the path, similar to Array.prototype.at(), where a negative number is the index counted from the end of the matching root. Defaults to the current root index.
  * @returns The basePath with embedded parameters.
  *
  * @example
@@ -92,17 +103,17 @@ export const baseRoutePath = (c: Context): string => matchedRoutes(c)[c.req.rout
  * app.route('/:sub', subApp)
  * ```
  */
-const basePathCacheMap: WeakMap<Context, string[]> = new WeakMap()
-export const basePath = (c: Context): string => {
-  const routeIndex = c.req.routeIndex
+const basePathCacheMap: WeakMap<Context, Record<number, string>> = new WeakMap()
+export const basePath = (c: Context, index?: number): string => {
+  index ??= c.req.routeIndex
 
   const cache = basePathCacheMap.get(c) || []
-  if (typeof cache[routeIndex] === 'string') {
-    return cache[routeIndex]
+  if (typeof cache[index] === 'string') {
+    return cache[index]
   }
 
   let result: string
-  const rp = baseRoutePath(c)
+  const rp = baseRoutePath(c, index)
   if (!/[:*]/.test(rp)) {
     result = rp
   } else {
@@ -123,7 +134,7 @@ export const basePath = (c: Context): string => {
     result = reqPath.substring(0, basePathLength)
   }
 
-  cache[routeIndex] = result
+  cache[index] = result
   basePathCacheMap.set(c, cache)
 
   return result