@@ -51,11 +51,13 @@ type ExpressRouter = Router & {
51
51
) => unknown ;
52
52
} ;
53
53
54
+ type RouteType = string | RegExp ;
55
+
54
56
/* Type used for pathing the express router prototype */
55
57
type Layer = {
56
58
match : ( path : string ) => boolean ;
57
59
handle_request : ( req : PatchedRequest , res : ExpressResponse , next : ( ) => void ) => void ;
58
- route ?: { path : string | RegExp } ;
60
+ route ?: { path : RouteType | RouteType [ ] } ;
59
61
path ?: string ;
60
62
} ;
61
63
@@ -273,11 +275,8 @@ function instrumentRouter(appOrRouter: ExpressRouter): void {
273
275
req . _reconstructedRoute = '' ;
274
276
}
275
277
276
- // If the layer's partial route has params, the route is stored in layer.route.
277
- // Since a route might be defined with a RegExp, we convert it toString to make sure we end up with a string
278
- const lrp = layer . route ?. path ;
279
- const isRegex = isRegExp ( lrp ) ;
280
- const layerRoutePath = isRegex ? lrp ?. toString ( ) : ( lrp as string ) ;
278
+ // If the layer's partial route has params, is a regex or an array, the route is stored in layer.route.
279
+ const { layerRoutePath, isRegex, numExtraSegments } : LayerRoutePathInfo = getLayerRoutePathString ( layer ) ;
281
280
282
281
// Otherwise, the hardcoded path (i.e. a partial route without params) is stored in layer.path
283
282
const partialRoute = layerRoutePath || layer . path || '' ;
@@ -301,7 +300,7 @@ function instrumentRouter(appOrRouter: ExpressRouter): void {
301
300
// Now we check if we are in the "last" part of the route. We determine this by comparing the
302
301
// number of URL segments from the original URL to that of our reconstructed parameterized URL.
303
302
// If we've reached our final destination, we update the transaction name.
304
- const urlLength = getNumberOfUrlSegments ( req . originalUrl || '' ) ;
303
+ const urlLength = getNumberOfUrlSegments ( req . originalUrl || '' ) + numExtraSegments ;
305
304
const routeLength = getNumberOfUrlSegments ( req . _reconstructedRoute ) ;
306
305
307
306
if ( urlLength === routeLength ) {
@@ -319,7 +318,48 @@ function instrumentRouter(appOrRouter: ExpressRouter): void {
319
318
} ;
320
319
}
321
320
321
+ type LayerRoutePathInfo = {
322
+ layerRoutePath ?: string ;
323
+ isRegex : boolean ;
324
+ numExtraSegments : number ;
325
+ } ;
326
+
327
+ /**
328
+ * Extracts and stringifies the layer's route which can either be a string with parameters (`users/:id`),
329
+ * a RegEx (`/test/`) or an array of strings and regexes (`['/path1', /\/path[2-5]/, /path/:id]`).
330
+ *
331
+ * @param layer the layer to extract the stringified route from
332
+ *
333
+ * @returns an object containing the stringified route, a flag determining if the route was a regex
334
+ * and the number of extra segments to the matched path that are additionally in the route,
335
+ * if the route was an array (defaults to 0).
336
+ */
337
+ function getLayerRoutePathString ( layer : Layer ) : LayerRoutePathInfo {
338
+ const lrp = layer . route ?. path ;
339
+
340
+ const isRegex = isRegExp ( lrp ) ;
341
+ const isArray = Array . isArray ( lrp ) ;
342
+
343
+ const numExtraSegments = isArray ? getNumberOfArrayUrlSegments ( lrp ) - getNumberOfUrlSegments ( layer . path || '' ) : 0 ;
344
+
345
+ const layerRoutePath = isArray ? lrp . join ( ',' ) : isRegex ? lrp ?. toString ( ) : ( lrp as string | undefined ) ;
346
+
347
+ return { layerRoutePath, isRegex, numExtraSegments } ;
348
+ }
349
+
350
+ /**
351
+ * Returns the number of URL segments in an array of routes
352
+ *
353
+ * Example: ['/api/test', /\/api\/post[0-9]/, '/users/:id/details`] -> 7
354
+ */
355
+ function getNumberOfArrayUrlSegments ( routesArray : RouteType [ ] ) : number {
356
+ return routesArray . reduce ( ( accNumSegments : number , currentRoute : RouteType ) => {
357
+ // array members can be a RegEx -> convert them toString
358
+ return accNumSegments + getNumberOfUrlSegments ( currentRoute . toString ( ) ) ;
359
+ } , 0 ) ;
360
+ }
361
+
322
362
function getNumberOfUrlSegments ( url : string ) : number {
323
363
// split at '/' or at '\/' to split regex urls correctly
324
- return url . split ( / \\ ? \/ / ) . filter ( s => s . length > 0 ) . length ;
364
+ return url . split ( / \\ ? \/ / ) . filter ( s => s . length > 0 && s !== ',' ) . length ;
325
365
}
0 commit comments