{"version":3,"file":"web-vitals.cjs","sources":["../../src/web-vitals.tsx"],"sourcesContent":["'use client';\nimport { Logger } from '@axiomhq/logging';\nimport * as React from 'react';\nimport { onLCP, onFID, onCLS, onINP, onFCP, onTTFB } from 'web-vitals';\nimport type { Metric } from 'web-vitals';\n\nconst SCALAR_ENTRY_PROPERTIES = [\n  'name',\n  'entryType',\n  'startTime',\n  'duration',\n  'renderTime',\n  'loadTime',\n  'size',\n  'id',\n  'url',\n  'value',\n  'hadRecentInput',\n  'lastInputTime',\n  'processingStart',\n  'processingEnd',\n  'interactionId',\n  'cancelable',\n  'activationStart',\n  'workerStart',\n  'redirectStart',\n  'redirectEnd',\n  'fetchStart',\n  'domainLookupStart',\n  'domainLookupEnd',\n  'connectStart',\n  'secureConnectionStart',\n  'connectEnd',\n  'requestStart',\n  'responseStart',\n  'responseEnd',\n  'transferSize',\n  'encodedBodySize',\n  'decodedBodySize',\n  'nextHopProtocol',\n  'type',\n] as const;\n\nconst RECT_PROPERTIES = ['x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left'] as const;\n\nfunction readProperty(value: object, property: string): unknown {\n  try {\n    return (value as Record<string, unknown>)[property];\n  } catch {\n    return undefined;\n  }\n}\n\nfunction normalizeNode(node: unknown): Record<string, string> | undefined {\n  if (typeof node !== 'object' || node === null) {\n    return undefined;\n  }\n\n  const result: Record<string, string> = {};\n  for (const property of ['nodeName', 'tagName', 'id', 'className'] as const) {\n    const value = readProperty(node, property);\n    if (typeof value === 'string' && value.length > 0) {\n      result[property] = value;\n    }\n  }\n\n  return Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction normalizeRect(rect: unknown): Record<string, number> | undefined {\n  if (typeof rect !== 'object' || rect === null) {\n    return undefined;\n  }\n\n  const result: Record<string, number> = {};\n  for (const property of RECT_PROPERTIES) {\n    const value = readProperty(rect, property);\n    if (typeof value === 'number') {\n      result[property] = value;\n    }\n  }\n\n  return Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction normalizeLayoutShiftSources(sources: unknown): Array<Record<string, unknown>> | undefined {\n  if (!Array.isArray(sources)) {\n    return undefined;\n  }\n\n  return sources.map((source) => {\n    if (typeof source !== 'object' || source === null) {\n      return {};\n    }\n\n    return {\n      node: normalizeNode(readProperty(source, 'node')),\n      previousRect: normalizeRect(readProperty(source, 'previousRect')),\n      currentRect: normalizeRect(readProperty(source, 'currentRect')),\n    };\n  });\n}\n\nfunction normalizePerformanceEntry(entry: PerformanceEntry): Record<string, unknown> {\n  const result: Record<string, unknown> = {};\n\n  for (const property of SCALAR_ENTRY_PROPERTIES) {\n    const value = readProperty(entry, property);\n    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n      result[property] = value;\n    }\n  }\n\n  const element = normalizeNode(readProperty(entry, 'element'));\n  if (element !== undefined) {\n    result.element = element;\n  }\n\n  const target = normalizeNode(readProperty(entry, 'target'));\n  if (target !== undefined) {\n    result.target = target;\n  }\n\n  const sources = normalizeLayoutShiftSources(readProperty(entry, 'sources'));\n  if (sources !== undefined) {\n    result.sources = sources;\n  }\n\n  return result;\n}\n\nexport function useReportWebVitals(pushMetrics: (metric: Metric) => void, flushMetrics: () => void) {\n  const pushMetricsRef = React.useRef(pushMetrics);\n  const flushMetricsRef = React.useRef(flushMetrics);\n\n  React.useEffect(() => {\n    const effectFlushMetrics = () => {\n      flushMetricsRef.current();\n    };\n\n    onCLS(pushMetricsRef.current);\n    onFID(pushMetricsRef.current);\n    onLCP(pushMetricsRef.current);\n    onINP(pushMetricsRef.current);\n    onFCP(pushMetricsRef.current);\n    onTTFB(pushMetricsRef.current);\n\n    document.addEventListener('visibilitychange', effectFlushMetrics);\n\n    return () => {\n      document.removeEventListener('visibilitychange', effectFlushMetrics);\n    };\n  }, []);\n}\n\nexport const transformWebVitalsMetric = (metric: Metric): Record<string, any> => {\n  return {\n    webVital: {\n      ...metric,\n      entries: metric.entries?.map(normalizePerformanceEntry) ?? [],\n    },\n    _time: new Date().getTime(),\n    source: 'web-vital',\n    path: window.location.pathname,\n  };\n};\n\nexport const createWebVitalsComponent = (logger: Logger) => {\n  const sendMetrics = (metric: Metric) => {\n    logger.raw(transformWebVitalsMetric(metric));\n  };\n\n  const flushMetrics = () => {\n    logger.flush();\n  };\n\n  return () => {\n    useReportWebVitals(sendMetrics, flushMetrics);\n\n    return <></>;\n  };\n};\n"],"names":["React","onCLS","onFID","onLCP","onINP","onFCP","onTTFB"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAMA;AAAgC;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AAEA;AAEA;AACE;AACE;AAAkD;AAElD;AAAO;AAEX;AAEA;AACE;AACE;AAAO;AAGT;AACA;AACE;AACA;AACE;AAAmB;AACrB;AAGF;AACF;AAEA;AACE;AACE;AAAO;AAGT;AACA;AACE;AACA;AACE;AAAmB;AACrB;AAGF;AACF;AAEA;AACE;AACE;AAAO;AAGT;AACE;AACE;AAAO;AAGT;AAAO;AAC2C;AACgB;AACF;AAAA;AAGpE;AAEA;AACE;AAEA;AACE;AACA;AACE;AAAmB;AACrB;AAGF;AACA;AACE;AAAiB;AAGnB;AACA;AACE;AAAgB;AAGlB;AACA;AACE;AAAiB;AAGnB;AACF;AAEO;AACL;AACA;AAEAA;AACE;AACE;AAAgB;AAGlBC;AACAC;AACAC;AACAC;AACAC;AACAC;AAEA;AAEA;AACE;AAAmE;AACrE;AAEJ;AAEO;AACL;AAAO;AACK;AACL;AACwD;AAAC;AAE5C;AACV;AACc;AAE1B;AAEO;AACL;AACE;AAA2C;AAG7C;AACE;AAAO;AAGT;AACE;AAEA;AAAS;AAEb;;;;"}