{"version":3,"file":"enrichers.cjs","names":[],"sources":["../src/enrichers.ts"],"sourcesContent":["type Headers = Record<string, string | string[] | undefined>;\n\nfunction get(headers: Headers, key: string): string | undefined {\n  const lower = key.toLowerCase();\n  for (const [k, v] of Object.entries(headers)) {\n    if (k.toLowerCase() === lower) {\n      return Array.isArray(v) ? v[0] : v;\n    }\n  }\n  return undefined;\n}\n\n// --- User Agent ---\n\nexport interface UserAgentAttributes {\n  'user_agent.raw': string;\n  'user_agent.browser'?: string;\n  'user_agent.os'?: string;\n  'user_agent.device'?: string;\n}\n\nconst BROWSER_RE = /(Firefox|OPR|Edg|Chrome|Safari|MSIE|Trident)[\\s/]?([\\d.]*)/;\nconst OS_RE =\n  /(Windows NT|Mac OS X|Linux|Android|iPhone OS|iPad|CrOS)[\\s]?([\\d._]*)/;\n\nfunction parseBrowser(ua: string): string | undefined {\n  const m = BROWSER_RE.exec(ua);\n  if (!m) return undefined;\n  const name =\n    m[1] === 'OPR'\n      ? 'Opera'\n      : m[1] === 'Edg'\n        ? 'Edge'\n        : m[1] === 'Trident'\n          ? 'IE'\n          : m[1];\n  return m[2] ? `${name} ${m[2]}` : name;\n}\n\nfunction parseOS(ua: string): string | undefined {\n  const m = OS_RE.exec(ua);\n  if (!m) return undefined;\n  const name =\n    m[1] === 'iPhone OS'\n      ? 'iOS'\n      : m[1] === 'Windows NT'\n        ? 'Windows'\n        : m[1] === 'Mac OS X'\n          ? 'macOS'\n          : m[1];\n  const ver = m[2]?.replaceAll('_', '.') || undefined;\n  return ver ? `${name} ${ver}` : name;\n}\n\nfunction parseDevice(ua: string): string | undefined {\n  if (/Mobi|Android.*Mobile|iPhone/.test(ua)) return 'mobile';\n  if (/iPad|Android(?!.*Mobile)|Tablet/.test(ua)) return 'tablet';\n  if (/Bot|Crawler|Spider|Lighthouse/i.test(ua)) return 'bot';\n  return 'desktop';\n}\n\nexport function userAgent(headers: Headers): UserAgentAttributes | undefined {\n  const raw = get(headers, 'user-agent');\n  if (!raw) return undefined;\n\n  const attrs: UserAgentAttributes = { 'user_agent.raw': raw };\n  const browser = parseBrowser(raw);\n  if (browser) attrs['user_agent.browser'] = browser;\n  const os = parseOS(raw);\n  if (os) attrs['user_agent.os'] = os;\n  const device = parseDevice(raw);\n  if (device) attrs['user_agent.device'] = device;\n\n  return attrs;\n}\n\n// --- Geo ---\n\nexport interface GeoAttributes {\n  'geo.country'?: string;\n  'geo.region'?: string;\n  'geo.city'?: string;\n  'geo.latitude'?: string;\n  'geo.longitude'?: string;\n}\n\nexport function geo(headers: Headers): GeoAttributes | undefined {\n  const country =\n    get(headers, 'x-vercel-ip-country') ?? get(headers, 'cf-ipcountry');\n  const region = get(headers, 'x-vercel-ip-country-region');\n  const city = get(headers, 'x-vercel-ip-city');\n  const latitude = get(headers, 'x-vercel-ip-latitude');\n  const longitude = get(headers, 'x-vercel-ip-longitude');\n\n  if (!country && !region && !city && !latitude && !longitude) return undefined;\n\n  const attrs: GeoAttributes = {};\n  if (country) attrs['geo.country'] = country;\n  if (region) attrs['geo.region'] = region;\n  if (city) {\n    try {\n      attrs['geo.city'] = decodeURIComponent(city);\n    } catch {\n      attrs['geo.city'] = city;\n    }\n  }\n  if (latitude) attrs['geo.latitude'] = latitude;\n  if (longitude) attrs['geo.longitude'] = longitude;\n\n  return attrs;\n}\n\n// --- Request Size ---\n\nexport interface RequestSizeAttributes {\n  'http.request.body.size'?: number;\n  'http.response.body.size'?: number;\n}\n\nconst DIGITS_RE = /^\\d+$/;\n\nfunction parseContentLength(value: string | undefined): number | undefined {\n  if (!value || !DIGITS_RE.test(value)) return undefined;\n  return Number(value);\n}\n\nexport function requestSize(\n  requestHeaders: Headers,\n  responseHeaders?: Headers,\n): RequestSizeAttributes | undefined {\n  const reqLen = get(requestHeaders, 'content-length');\n  const resLen = responseHeaders\n    ? get(responseHeaders, 'content-length')\n    : undefined;\n\n  if (!reqLen && !resLen) return undefined;\n\n  const attrs: RequestSizeAttributes = {};\n  const reqBytes = parseContentLength(reqLen);\n  if (reqBytes !== undefined) attrs['http.request.body.size'] = reqBytes;\n  const resBytes = parseContentLength(resLen);\n  if (resBytes !== undefined) attrs['http.response.body.size'] = resBytes;\n\n  return Object.keys(attrs).length > 0 ? attrs : undefined;\n}\n"],"mappings":"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"}