{"version":3,"file":"utils.js","sources":["../../../../src/integrations/express/utils.ts"],"sourcesContent":["/**\n * Platform-portable Express tracing integration.\n *\n * @module\n *\n * This Sentry integration is a derivative work based on the OpenTelemetry\n * Express instrumentation.\n *\n * <https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-express>\n *\n * Extended under the terms of the Apache 2.0 license linked below:\n *\n * ----\n *\n * Copyright The OpenTelemetry Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { SpanAttributes } from '../../types-hoist/span';\nimport { getStoredLayers } from './request-layer-store';\nimport type {\n  ExpressIntegrationOptions,\n  ExpressLayer,\n  ExpressLayerType,\n  ExpressRequest,\n  LayerPathSegment,\n  MiddlewareError,\n  ExpressRouterv4,\n  ExpressExportv5,\n  ExpressExportv4,\n} from './types';\nimport {\n  ATTR_EXPRESS_NAME,\n  ATTR_EXPRESS_TYPE,\n  ExpressLayerType_MIDDLEWARE,\n  ExpressLayerType_REQUEST_HANDLER,\n  ExpressLayerType_ROUTER,\n} from './types';\nimport { stringMatchesSomePattern } from '../../utils/string';\n\n/**\n * Converts a user-provided error value into an error and error message pair\n *\n * @param error - User-provided error value\n * @returns Both an Error or string representation of the value and an error message\n */\nexport const asErrorAndMessage = (error: unknown): [string | Error, string] =>\n  error instanceof Error ? [error, error.message] : [String(error), String(error)];\n\n/**\n * Checks if a route contains parameter patterns (e.g., :id, :userId)\n * which are valid even if they don't exactly match the original URL\n */\nexport function isRoutePattern(route: string): boolean {\n  return route.includes(':') || route.includes('*');\n}\n\n/**\n * Parse express layer context to retrieve a name and attributes.\n * @param route The route of the layer\n * @param layer Express layer\n * @param [layerPath] if present, the path on which the layer has been mounted\n */\nexport const getLayerMetadata = (\n  route: string,\n  layer: ExpressLayer,\n  layerPath?: string,\n): {\n  attributes: SpanAttributes & { [ATTR_EXPRESS_NAME]: string; [ATTR_EXPRESS_TYPE]: ExpressLayerType };\n  name: string;\n} => {\n  if (layer.name === 'router') {\n    const maybeRouterPath = getRouterPath('', layer);\n    const extractedRouterPath = maybeRouterPath ? maybeRouterPath : layerPath || route || '/';\n\n    return {\n      attributes: {\n        [ATTR_EXPRESS_NAME]: extractedRouterPath,\n        [ATTR_EXPRESS_TYPE]: ExpressLayerType_ROUTER,\n      },\n      name: `router - ${extractedRouterPath}`,\n    };\n  } else if (layer.name === 'bound dispatch' || layer.name === 'handle') {\n    return {\n      attributes: {\n        [ATTR_EXPRESS_NAME]: (route || layerPath) ?? 'request handler',\n        [ATTR_EXPRESS_TYPE]: ExpressLayerType_REQUEST_HANDLER,\n      },\n      name: `request handler${layer.path ? ` - ${route || layerPath}` : ''}`,\n    };\n  } else {\n    return {\n      attributes: {\n        [ATTR_EXPRESS_NAME]: layer.name,\n        [ATTR_EXPRESS_TYPE]: ExpressLayerType_MIDDLEWARE,\n      },\n      name: `middleware - ${layer.name}`,\n    };\n  }\n};\n\n/**\n * Recursively search the router path from layer stack\n * @param path The path to reconstruct\n * @param layer The layer to reconstruct from\n * @returns The reconstructed path\n */\nexport const getRouterPath = (path: string, layer: ExpressLayer): string => {\n  const stackLayer = Array.isArray(layer.handle?.stack) ? layer.handle?.stack?.[0] : undefined;\n\n  if (stackLayer?.route?.path) {\n    return `${path}${stackLayer.route.path}`;\n  }\n\n  if (stackLayer && Array.isArray(stackLayer?.handle?.stack)) {\n    return getRouterPath(path, stackLayer);\n  }\n\n  return path;\n};\n\n/**\n * Check whether the given request is ignored by configuration\n * It will not re-throw exceptions from `list` provided by the client\n * @param constant e.g URL of request\n * @param [list] List of ignore patterns\n * @param [onException] callback for doing something when an exception has\n *     occurred\n */\nexport type ExpressIsLayerIgnoredOptions = Pick<ExpressIntegrationOptions, 'ignoreLayersType' | 'ignoreLayers'>;\nexport const isLayerIgnored = (\n  name: string,\n  type: ExpressLayerType,\n  config?: ExpressIsLayerIgnoredOptions,\n): boolean => {\n  if (Array.isArray(config?.ignoreLayersType) && config?.ignoreLayersType?.includes(type)) {\n    return true;\n  }\n  if (!Array.isArray(config?.ignoreLayers)) {\n    return false;\n  }\n  try {\n    return stringMatchesSomePattern(name, config.ignoreLayers, true);\n  } catch {}\n\n  return false;\n};\n\n/**\n * Extracts the actual matched route from Express request for OpenTelemetry instrumentation.\n * Returns the route that should be used as the http.route attribute.\n *\n * @param req - The Express request object with layers store\n * @param constructedRoute - The constructed route from `getConstructedRoute`\n * @returns The matched route string or undefined if no valid route is found\n */\nexport function getActualMatchedRoute(req: ExpressRequest, constructedRoute: string): string | undefined {\n  const layersStore = getStoredLayers(req);\n\n  // If no layers are stored, no route can be determined\n  if (layersStore.length === 0) {\n    return undefined;\n  }\n\n  // Handle root path case - if all paths are root, only return root if originalUrl is also root\n  // The layer store also includes root paths in case a non-existing url was requested\n  if (layersStore.every(path => path === '/')) {\n    return req.originalUrl === '/' ? '/' : undefined;\n  }\n\n  if (constructedRoute === '*') {\n    return constructedRoute;\n  }\n\n  // For RegExp routes or route arrays, return the constructed route\n  // This handles the case where the route is defined using RegExp or an array\n  if (\n    constructedRoute.includes('/') &&\n    (constructedRoute.includes(',') ||\n      constructedRoute.includes('\\\\') ||\n      constructedRoute.includes('*') ||\n      constructedRoute.includes('['))\n  ) {\n    return constructedRoute;\n  }\n\n  // Ensure route starts with '/' if it doesn't already\n  const normalizedRoute = constructedRoute.startsWith('/') ? constructedRoute : `/${constructedRoute}`;\n\n  // Validate that this appears to be a matched route\n  // A route is considered matched if:\n  // 1. We have a constructed route\n  // 2. The original URL matches or starts with our route pattern\n  const isValidRoute =\n    normalizedRoute.length > 0 &&\n    (req.originalUrl === normalizedRoute ||\n      req.originalUrl.startsWith(normalizedRoute) ||\n      isRoutePattern(normalizedRoute));\n\n  return isValidRoute ? normalizedRoute : undefined;\n}\n\nexport function getConstructedRoute(req: ExpressRequest) {\n  const layersStore: string[] = getStoredLayers(req);\n\n  let constructedRoute: string = '';\n  for (const path of layersStore) {\n    if (path === '/' || path === '/*') {\n      continue;\n    }\n    constructedRoute += !constructedRoute || constructedRoute.endsWith('/') ? path : `/${path}`;\n  }\n\n  return constructedRoute.replace(/\\/{2,}/g, '/');\n}\n\nexport const getLayerPath = (args: unknown[]): string | undefined => {\n  const firstArg = args[0];\n\n  if (Array.isArray(firstArg)) {\n    return firstArg.map(arg => extractLayerPathSegment(arg) || '').join(',');\n  }\n\n  return extractLayerPathSegment(firstArg as LayerPathSegment);\n};\n\nconst extractLayerPathSegment = (arg: LayerPathSegment): string | undefined =>\n  typeof arg === 'string' ? arg : arg instanceof RegExp || typeof arg === 'number' ? String(arg) : undefined;\n\n// v5 we instrument Router.prototype\n// v4 we instrument Router itself\nexport const isExpressWithRouterPrototype = (express: unknown): express is ExpressExportv5 =>\n  isExpressRouterPrototype((express as ExpressExportv5)?.Router?.prototype);\n\n// In Express v4, Router is a function (not a plain object), so we need to accept both\nconst isExpressRouterPrototype = (routerProto?: unknown): routerProto is ExpressRouterv4 =>\n  (typeof routerProto === 'object' || typeof routerProto === 'function') &&\n  !!routerProto &&\n  'route' in routerProto &&\n  typeof (routerProto as ExpressRouterv4).route === 'function';\n\nexport const isExpressWithoutRouterPrototype = (express: unknown): express is ExpressExportv4 =>\n  isExpressRouterPrototype((express as ExpressExportv4).Router) && !isExpressWithRouterPrototype(express);\n\nfunction getStatusCodeFromResponse(error: MiddlewareError): number {\n  const statusCode = error.status || error.statusCode || error.status_code || error.output?.statusCode;\n  return statusCode ? parseInt(statusCode as string, 10) : 500;\n}\n\n/** Returns true if response code is internal server error */\nexport function defaultShouldHandleError(error: MiddlewareError): boolean {\n  const status = getStatusCodeFromResponse(error);\n  return status >= 500;\n}\n"],"names":[],"mappings":"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"}