datadog-ux-utils
Version: 
Datadog RUM focused UX & performance toolkit: API guards (retry, breaker, rate), React telemetry (error boundary, profiler, Suspense), web vitals & resource observers, offline queues.
1 lines • 63.1 kB
Source Map (JSON)
{"version":3,"file":"useComponentTelemetry-BjZuufzp.cjs","sources":["../src/react/ErrorBoundary.tsx","../node_modules/react/cjs/react-jsx-runtime.production.js","../node_modules/react/cjs/react-jsx-runtime.development.js","../node_modules/react/jsx-runtime.js","../src/react/RenderDetector.tsx","../src/react/RenderProfiler.tsx","../src/react/suspenseWatch.tsx","../src/react/useGuardFetch.ts","../src/react/useGuardedCall.ts","../src/react/useComponentTelemetry.tsx"],"sourcesContent":["/**\n * @file ErrorBoundary.tsx\n * @description React error boundary that catches errors in child components and reports them to Datadog RUM.\n */\nimport { Component, ErrorInfo, ReactNode } from \"react\";\nimport { datadogRum } from \"@datadog/browser-rum\";\nimport { getUxConfig } from \"../config.ts\";\n\ntype Props = {\n  children: ReactNode;\n  fallback?: ReactNode;\n  name?: string;\n};\n\n/**\n * React error boundary that catches errors in its child components and reports them to Datadog RUM.\n *\n * @remarks\n * Use this component to wrap any part of your React app where you want to catch and report errors.\n * When an error is caught, the fallback UI is rendered (if provided).\n *\n * @example\n * ```tsx\n * import { ErrorBoundary } from 'datadog-ux-utils/react';\n *\n * <ErrorBoundary name=\"AppRoot\" fallback={<h1>Something broke.</h1>}>\n *   <App />\n * </ErrorBoundary>\n * ```\n *\n * @param props -\n *  - `children`: The subtree to protect with the error boundary.\n *  - `fallback`: Optional React node to render when an error is caught.\n *  - `name`: Optional identifier for this boundary (included in telemetry).\n */\nexport class ErrorBoundary extends Component<Props, { hasError: boolean }> {\n  state = { hasError: false };\n\n  /**\n   * Updates state so the next render shows the fallback UI.\n   */\n  static getDerivedStateFromError() {\n    return { hasError: true };\n  }\n\n  /**\n   * Reports the error to Datadog RUM with boundary name, stack, and app name.\n   * @param error - The error thrown by a child component.\n   * @param info - React error info (component stack).\n   */\n  componentDidCatch(error: Error, info: ErrorInfo) {\n    const { name } = this.props;\n    datadogRum.addError(error, {\n      boundary: name ?? \"ErrorBoundary\",\n      componentStack: info.componentStack,\n      app: getUxConfig().appName,\n    });\n  }\n\n  /**\n   * Renders the fallback UI if an error was caught, otherwise renders children.\n   */\n  render() {\n    if (this.state.hasError) return this.props.fallback ?? null;\n    return this.props.children;\n  }\n}\n","/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n  REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n  var key = null;\n  void 0 !== maybeKey && (key = \"\" + maybeKey);\n  void 0 !== config.key && (key = \"\" + config.key);\n  if (\"key\" in config) {\n    maybeKey = {};\n    for (var propName in config)\n      \"key\" !== propName && (maybeKey[propName] = config[propName]);\n  } else maybeKey = config;\n  config = maybeKey.ref;\n  return {\n    $$typeof: REACT_ELEMENT_TYPE,\n    type: type,\n    key: key,\n    ref: void 0 !== config ? config : null,\n    props: maybeKey\n  };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","/**\n * @license React\n * react-jsx-runtime.development.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\n\"production\" !== process.env.NODE_ENV &&\n  (function () {\n    function getComponentNameFromType(type) {\n      if (null == type) return null;\n      if (\"function\" === typeof type)\n        return type.$$typeof === REACT_CLIENT_REFERENCE\n          ? null\n          : type.displayName || type.name || null;\n      if (\"string\" === typeof type) return type;\n      switch (type) {\n        case REACT_FRAGMENT_TYPE:\n          return \"Fragment\";\n        case REACT_PROFILER_TYPE:\n          return \"Profiler\";\n        case REACT_STRICT_MODE_TYPE:\n          return \"StrictMode\";\n        case REACT_SUSPENSE_TYPE:\n          return \"Suspense\";\n        case REACT_SUSPENSE_LIST_TYPE:\n          return \"SuspenseList\";\n        case REACT_ACTIVITY_TYPE:\n          return \"Activity\";\n      }\n      if (\"object\" === typeof type)\n        switch (\n          (\"number\" === typeof type.tag &&\n            console.error(\n              \"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue.\"\n            ),\n          type.$$typeof)\n        ) {\n          case REACT_PORTAL_TYPE:\n            return \"Portal\";\n          case REACT_CONTEXT_TYPE:\n            return (type.displayName || \"Context\") + \".Provider\";\n          case REACT_CONSUMER_TYPE:\n            return (type._context.displayName || \"Context\") + \".Consumer\";\n          case REACT_FORWARD_REF_TYPE:\n            var innerType = type.render;\n            type = type.displayName;\n            type ||\n              ((type = innerType.displayName || innerType.name || \"\"),\n              (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n            return type;\n          case REACT_MEMO_TYPE:\n            return (\n              (innerType = type.displayName || null),\n              null !== innerType\n                ? innerType\n                : getComponentNameFromType(type.type) || \"Memo\"\n            );\n          case REACT_LAZY_TYPE:\n            innerType = type._payload;\n            type = type._init;\n            try {\n              return getComponentNameFromType(type(innerType));\n            } catch (x) {}\n        }\n      return null;\n    }\n    function testStringCoercion(value) {\n      return \"\" + value;\n    }\n    function checkKeyStringCoercion(value) {\n      try {\n        testStringCoercion(value);\n        var JSCompiler_inline_result = !1;\n      } catch (e) {\n        JSCompiler_inline_result = !0;\n      }\n      if (JSCompiler_inline_result) {\n        JSCompiler_inline_result = console;\n        var JSCompiler_temp_const = JSCompiler_inline_result.error;\n        var JSCompiler_inline_result$jscomp$0 =\n          (\"function\" === typeof Symbol &&\n            Symbol.toStringTag &&\n            value[Symbol.toStringTag]) ||\n          value.constructor.name ||\n          \"Object\";\n        JSCompiler_temp_const.call(\n          JSCompiler_inline_result,\n          \"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.\",\n          JSCompiler_inline_result$jscomp$0\n        );\n        return testStringCoercion(value);\n      }\n    }\n    function getTaskName(type) {\n      if (type === REACT_FRAGMENT_TYPE) return \"<>\";\n      if (\n        \"object\" === typeof type &&\n        null !== type &&\n        type.$$typeof === REACT_LAZY_TYPE\n      )\n        return \"<...>\";\n      try {\n        var name = getComponentNameFromType(type);\n        return name ? \"<\" + name + \">\" : \"<...>\";\n      } catch (x) {\n        return \"<...>\";\n      }\n    }\n    function getOwner() {\n      var dispatcher = ReactSharedInternals.A;\n      return null === dispatcher ? null : dispatcher.getOwner();\n    }\n    function UnknownOwner() {\n      return Error(\"react-stack-top-frame\");\n    }\n    function hasValidKey(config) {\n      if (hasOwnProperty.call(config, \"key\")) {\n        var getter = Object.getOwnPropertyDescriptor(config, \"key\").get;\n        if (getter && getter.isReactWarning) return !1;\n      }\n      return void 0 !== config.key;\n    }\n    function defineKeyPropWarningGetter(props, displayName) {\n      function warnAboutAccessingKey() {\n        specialPropKeyWarningShown ||\n          ((specialPropKeyWarningShown = !0),\n          console.error(\n            \"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)\",\n            displayName\n          ));\n      }\n      warnAboutAccessingKey.isReactWarning = !0;\n      Object.defineProperty(props, \"key\", {\n        get: warnAboutAccessingKey,\n        configurable: !0\n      });\n    }\n    function elementRefGetterWithDeprecationWarning() {\n      var componentName = getComponentNameFromType(this.type);\n      didWarnAboutElementRef[componentName] ||\n        ((didWarnAboutElementRef[componentName] = !0),\n        console.error(\n          \"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.\"\n        ));\n      componentName = this.props.ref;\n      return void 0 !== componentName ? componentName : null;\n    }\n    function ReactElement(\n      type,\n      key,\n      self,\n      source,\n      owner,\n      props,\n      debugStack,\n      debugTask\n    ) {\n      self = props.ref;\n      type = {\n        $$typeof: REACT_ELEMENT_TYPE,\n        type: type,\n        key: key,\n        props: props,\n        _owner: owner\n      };\n      null !== (void 0 !== self ? self : null)\n        ? Object.defineProperty(type, \"ref\", {\n            enumerable: !1,\n            get: elementRefGetterWithDeprecationWarning\n          })\n        : Object.defineProperty(type, \"ref\", { enumerable: !1, value: null });\n      type._store = {};\n      Object.defineProperty(type._store, \"validated\", {\n        configurable: !1,\n        enumerable: !1,\n        writable: !0,\n        value: 0\n      });\n      Object.defineProperty(type, \"_debugInfo\", {\n        configurable: !1,\n        enumerable: !1,\n        writable: !0,\n        value: null\n      });\n      Object.defineProperty(type, \"_debugStack\", {\n        configurable: !1,\n        enumerable: !1,\n        writable: !0,\n        value: debugStack\n      });\n      Object.defineProperty(type, \"_debugTask\", {\n        configurable: !1,\n        enumerable: !1,\n        writable: !0,\n        value: debugTask\n      });\n      Object.freeze && (Object.freeze(type.props), Object.freeze(type));\n      return type;\n    }\n    function jsxDEVImpl(\n      type,\n      config,\n      maybeKey,\n      isStaticChildren,\n      source,\n      self,\n      debugStack,\n      debugTask\n    ) {\n      var children = config.children;\n      if (void 0 !== children)\n        if (isStaticChildren)\n          if (isArrayImpl(children)) {\n            for (\n              isStaticChildren = 0;\n              isStaticChildren < children.length;\n              isStaticChildren++\n            )\n              validateChildKeys(children[isStaticChildren]);\n            Object.freeze && Object.freeze(children);\n          } else\n            console.error(\n              \"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.\"\n            );\n        else validateChildKeys(children);\n      if (hasOwnProperty.call(config, \"key\")) {\n        children = getComponentNameFromType(type);\n        var keys = Object.keys(config).filter(function (k) {\n          return \"key\" !== k;\n        });\n        isStaticChildren =\n          0 < keys.length\n            ? \"{key: someKey, \" + keys.join(\": ..., \") + \": ...}\"\n            : \"{key: someKey}\";\n        didWarnAboutKeySpread[children + isStaticChildren] ||\n          ((keys =\n            0 < keys.length ? \"{\" + keys.join(\": ..., \") + \": ...}\" : \"{}\"),\n          console.error(\n            'A props object containing a \"key\" prop is being spread into JSX:\\n  let props = %s;\\n  <%s {...props} />\\nReact keys must be passed directly to JSX without using spread:\\n  let props = %s;\\n  <%s key={someKey} {...props} />',\n            isStaticChildren,\n            children,\n            keys,\n            children\n          ),\n          (didWarnAboutKeySpread[children + isStaticChildren] = !0));\n      }\n      children = null;\n      void 0 !== maybeKey &&\n        (checkKeyStringCoercion(maybeKey), (children = \"\" + maybeKey));\n      hasValidKey(config) &&\n        (checkKeyStringCoercion(config.key), (children = \"\" + config.key));\n      if (\"key\" in config) {\n        maybeKey = {};\n        for (var propName in config)\n          \"key\" !== propName && (maybeKey[propName] = config[propName]);\n      } else maybeKey = config;\n      children &&\n        defineKeyPropWarningGetter(\n          maybeKey,\n          \"function\" === typeof type\n            ? type.displayName || type.name || \"Unknown\"\n            : type\n        );\n      return ReactElement(\n        type,\n        children,\n        self,\n        source,\n        getOwner(),\n        maybeKey,\n        debugStack,\n        debugTask\n      );\n    }\n    function validateChildKeys(node) {\n      \"object\" === typeof node &&\n        null !== node &&\n        node.$$typeof === REACT_ELEMENT_TYPE &&\n        node._store &&\n        (node._store.validated = 1);\n    }\n    var React = require(\"react\"),\n      REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n      REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n      REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n      REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n      REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\");\n    Symbol.for(\"react.provider\");\n    var REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n      REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n      REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n      REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n      REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n      REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n      REACT_LAZY_TYPE = Symbol.for(\"react.lazy\"),\n      REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\"),\n      REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\"),\n      ReactSharedInternals =\n        React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n      hasOwnProperty = Object.prototype.hasOwnProperty,\n      isArrayImpl = Array.isArray,\n      createTask = console.createTask\n        ? console.createTask\n        : function () {\n            return null;\n          };\n    React = {\n      react_stack_bottom_frame: function (callStackForError) {\n        return callStackForError();\n      }\n    };\n    var specialPropKeyWarningShown;\n    var didWarnAboutElementRef = {};\n    var unknownOwnerDebugStack = React.react_stack_bottom_frame.bind(\n      React,\n      UnknownOwner\n    )();\n    var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));\n    var didWarnAboutKeySpread = {};\n    exports.Fragment = REACT_FRAGMENT_TYPE;\n    exports.jsx = function (type, config, maybeKey, source, self) {\n      var trackActualOwner =\n        1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;\n      return jsxDEVImpl(\n        type,\n        config,\n        maybeKey,\n        !1,\n        source,\n        self,\n        trackActualOwner\n          ? Error(\"react-stack-top-frame\")\n          : unknownOwnerDebugStack,\n        trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask\n      );\n    };\n    exports.jsxs = function (type, config, maybeKey, source, self) {\n      var trackActualOwner =\n        1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;\n      return jsxDEVImpl(\n        type,\n        config,\n        maybeKey,\n        !0,\n        source,\n        self,\n        trackActualOwner\n          ? Error(\"react-stack-top-frame\")\n          : unknownOwnerDebugStack,\n        trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask\n      );\n    };\n  })();\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n  module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n  module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","/**\n * @file RenderDetector.tsx\n * @description Detects React render hotspots by tracking commit frequency and render cost, reporting to telemetry when thresholds are exceeded.\n */\nimport React, {\n  Profiler as ReactProfiler,\n  ProfilerOnRenderCallback,\n  ReactNode,\n  useMemo,\n  useRef,\n} from \"react\";\nimport { addAction } from \"../datadog.ts\"; // Safe, no-op if your init gates telemetry\n\n/**\n * Options controlling when a subtree is considered a render hotspot.\n */\nexport type RenderDetectorOptions = {\n  /**\n   * Enable the detector. Default:\n   *  - In development: true\n   *  - In production:  false\n   */\n  enabled?: boolean;\n\n  /**\n   * Size of the sliding window (in milliseconds) over which commit metrics\n   * are computed. Default: 2000ms (2 seconds).\n   */\n  windowMs?: number;\n\n  /**\n   * Threshold for commits per second that flags a hotspot.\n   * Example: if set to 5, more than 5 commits/sec within the window triggers.\n   * Default: 5 commits/sec.\n   */\n  commitsPerSecThreshold?: number;\n\n  /**\n   * Threshold for total render time per second (in milliseconds) that flags a hotspot.\n   * Example: if set to 24, more than 24ms of render work per second within the window triggers.\n   * Default: 24ms/sec (roughly > 40% of a 60fps frame budget).\n   */\n  renderMsPerSecThreshold?: number;\n\n  /**\n   * Minimum number of commits within the window before evaluating thresholds.\n   * Prevents noise from one-off commits. Default: 3 commits.\n   */\n  minCommits?: number;\n\n  /**\n   * Cooldown period (ms) after a hotspot is reported before reporting again\n   * for the same detector. Default: 5000ms (5s).\n   */\n  cooldownMs?: number;\n\n  /**\n   * Optional sample rate (0–100) for sending telemetry to DataDog via `addAction`.\n   * Default: 20 (%). Set to 0 to disable telemetry.\n   */\n  telemetrySampleRate?: number;\n\n  /**\n   * Action name emitted to DataDog when a hotspot is detected.\n   * Default: \"render_hotspot\".\n   */\n  telemetryActionName?: string;\n\n  /**\n   * Optional callback invoked when a hotspot is detected.\n   * Use this to log locally, show a dev toast, etc.\n   */\n  onHotspot?: (info: HotspotInfo) => void;\n\n  /**\n   * Include React Profiler baseDuration and phase in telemetry.\n   * Default: true. If false, only time-based aggregates are sent.\n   */\n  includeProfilerDetails?: boolean;\n\n  /**\n   * Arbitrary static context to include on every hotspot report.\n   * (Kept shallow to avoid heavy serialization.)\n   */\n  context?: Record<string, unknown>;\n};\n\n/**\n * Information passed to `onHotspot` and included in telemetry.\n */\nexport type HotspotInfo = {\n  /** The logical id of this detector (component name, route name, etc.). */\n  id: string;\n  /** Commits/sec over the sliding window. */\n  commitsPerSec: number;\n  /** Total render ms per sec (sum of actualDuration) over the window. */\n  renderMsPerSec: number;\n  /** Number of commits observed within the window. */\n  commitsInWindow: number;\n  /** Sliding window length (ms). */\n  windowMs: number;\n  /** Which thresholds were exceeded. */\n  reasons: Array<\"commits_per_sec\" | \"render_ms_per_sec\">;\n  /** Timestamp (ms since navigation start) of the last commit considered. */\n  at: number;\n  /** Optional extra from the last commit (phase, baseDuration). */\n  lastCommit?: {\n    phase: \"mount\" | \"update\" | \"nested-update\" | \"unknown\";\n    actualDurationMs: number;\n    baseDurationMs?: number;\n  };\n  /** Static context passed via props.context (if any). */\n  context?: Record<string, unknown>;\n};\n\n/**\n * Props for RenderDetector.\n */\nexport type RenderDetectorProps = {\n  /**\n   * A stable identifier for the subtree you're observing.\n   * Keep this short and human-readable; it will appear in logs/telemetry.\n   */\n  id: string;\n\n  /**\n   * Optional options; see `RenderDetectorOptions` for details.\n   */\n  options?: RenderDetectorOptions;\n\n  /**\n   * The subtree to measure.\n   */\n  children: ReactNode;\n\n  /**\n   * (Test only) Optional Profiler callback spy.\n   */\n  __onRenderSpy?: ProfilerOnRenderCallback;\n};\n\n/**\n * RenderDetector\n * --------------\n * Wraps a subtree in a React Profiler, tracks commit frequency and render cost in a sliding window,\n * and reports when thresholds are exceeded. Intended primarily for development; can be force-enabled\n * in production for targeted diagnostics.\n *\n * ## What it measures\n * - **commits/sec** over the last `windowMs` (default 2s)\n * - **render ms/sec** (sum of `actualDuration`) over the last `windowMs`\n *\n * ## When it reports\n * - When `commitsPerSec > commitsPerSecThreshold` OR\n * - When `renderMsPerSec > renderMsPerSecThreshold`\n * - Only after at least `minCommits` commits within the window\n * - At most once per `cooldownMs`\n *\n * ## Telemetry\n * - Sends a single DataDog action (sampled) per hotspot with:\n *   `{ id, commitsPerSec, renderMsPerSec, commitsInWindow, reasons, windowMs, ... }`\n * - Defaults: `telemetryActionName = \"render_hotspot\"`, `telemetrySampleRate = 20`\n * - You can disable telemetry by setting `telemetrySampleRate: 0`\n *\n * ## Example\n * ```tsx\n * import { RenderDetector } from \"@milliman/dd-ux-utils/react/dev/RenderDetector\";\n *\n * function ResultsPanel() {\n *   return (\n *     <RenderDetector\n *       id=\"ResultsPanel\"\n *       options={{\n *         commitsPerSecThreshold: 6,\n *         renderMsPerSecThreshold: 30,\n *         onHotspot: info => console.warn(\"Render hotspot:\", info),\n *       }}\n *     >\n *       <ExpensiveResults />\n *     </RenderDetector>\n *   );\n * }\n * ```\n *\n * ## Example (force-enable in production, different thresholds)\n * ```tsx\n * <RenderDetector\n *   id=\"SearchResults\"\n *   options={{\n *     enabled: true,                   // opt-in for prod\n *     windowMs: 3000,\n *     commitsPerSecThreshold: 4,\n *     renderMsPerSecThreshold: 20,\n *     telemetrySampleRate: 10,\n *     context: { route: \"/search\" },\n *   }}\n * >\n *   <SearchResults />\n * </RenderDetector>\n * ```\n */\nexport function RenderDetector({\n  id,\n  options,\n  children,\n  __onRenderSpy,\n}: RenderDetectorProps) {\n  const opts = useMemo<Required<RenderDetectorOptions>>(\n    () => ({\n      enabled:\n        options?.enabled ??\n        (typeof process !== \"undefined\" &&\n          process.env &&\n          process.env.NODE_ENV !== \"production\"),\n      windowMs: options?.windowMs ?? 2000,\n      commitsPerSecThreshold: options?.commitsPerSecThreshold ?? 5,\n      renderMsPerSecThreshold: options?.renderMsPerSecThreshold ?? 24,\n      minCommits: options?.minCommits ?? 3,\n      cooldownMs: options?.cooldownMs ?? 5000,\n      telemetrySampleRate:\n        typeof options?.telemetrySampleRate === \"number\"\n          ? clampPct(options.telemetrySampleRate)\n          : 20,\n      telemetryActionName: options?.telemetryActionName ?? \"render_hotspot\",\n      onHotspot: options?.onHotspot ?? noop,\n      includeProfilerDetails: options?.includeProfilerDetails ?? true,\n      context: options?.context ?? {},\n    }),\n    [options]\n  );\n\n  // A ring buffer would be fine; an array with head pruning is simpler and tiny here.\n  const commitsRef = useRef<\n    Array<{\n      t: number;\n      d: number;\n      bd?: number;\n      ph: NonNullable<HotspotInfo[\"lastCommit\"]>[\"phase\"];\n    }>\n  >([]);\n  const lastReportAtRef = useRef(0);\n\n  const onRender: ProfilerOnRenderCallback = (\n    _id,\n    phase,\n    actualDuration,\n    baseDuration,\n    startTime,\n    _commitTime\n  ) => {\n    if (!opts.enabled) return;\n\n    const now = startTime; // perf timestamp when render started (ms since page load)\n    const windowStart = now - opts.windowMs;\n\n    // Record this commit\n    commitsRef.current.push({\n      t: now,\n      d: actualDuration,\n      bd: opts.includeProfilerDetails ? baseDuration : undefined,\n      ph: (phase as any) ?? \"unknown\",\n    });\n\n    // Prune old commits outside the sliding window\n    const buf = commitsRef.current;\n    while (buf.length && buf[0].t < windowStart) buf.shift();\n\n    // Debug: log buffer and thresholds\n    if (process.env.NODE_ENV === \"test\") {\n      // eslint-disable-next-line no-console\n      console.log(\n        \"[RenderDetector] buffer:\",\n        buf.map((c) => c.t),\n        \"commitsInWindow:\",\n        buf.length\n      );\n    }\n\n    // Bail early if not enough signal\n    if (buf.length < opts.minCommits) return;\n\n    // Aggregate metrics over the current window\n    const commitsInWindow = buf.length;\n    const durationSum = buf.reduce((acc, c) => acc + c.d, 0);\n    // Normalize to per-second rates\n    const seconds = opts.windowMs / 1000;\n    const commitsPerSec = commitsInWindow / seconds;\n    const renderMsPerSec = durationSum / seconds;\n\n    if (process.env.NODE_ENV === \"test\") {\n      // eslint-disable-next-line no-console\n      console.log(\"[RenderDetector] metrics:\", {\n        commitsPerSec,\n        renderMsPerSec,\n        commitsInWindow,\n      });\n    }\n\n    const reasons: HotspotInfo[\"reasons\"] = [];\n    if (commitsPerSec > opts.commitsPerSecThreshold)\n      reasons.push(\"commits_per_sec\");\n    if (renderMsPerSec > opts.renderMsPerSecThreshold)\n      reasons.push(\"render_ms_per_sec\");\n    if (reasons.length === 0) return;\n\n    // Cooldown gating\n    const lastAt = lastReportAtRef.current;\n    if (now - lastAt < opts.cooldownMs) return;\n    lastReportAtRef.current = now;\n\n    if (process.env.NODE_ENV === \"test\") {\n      // eslint-disable-next-line no-console\n      console.log(\"[RenderDetector] HOTSPOT!\", { reasons, now });\n    }\n\n    const last = buf[buf.length - 1];\n    const info: HotspotInfo = {\n      id,\n      commitsPerSec: round2(commitsPerSec),\n      renderMsPerSec: round2(renderMsPerSec),\n      commitsInWindow,\n      windowMs: opts.windowMs,\n      reasons,\n      at: now,\n      lastCommit: opts.includeProfilerDetails\n        ? {\n            phase: last.ph,\n            actualDurationMs: round2(last.d),\n            baseDurationMs: last.bd != null ? round2(last.bd) : undefined,\n          }\n        : undefined,\n      context: opts.context,\n    };\n\n    // Local callback\n    try {\n      opts.onHotspot(info);\n    } catch {\n      // never let diagnostics break app code\n    }\n\n    // Optional telemetry\n    if (opts.telemetrySampleRate > 0 && passSample(opts.telemetrySampleRate)) {\n      try {\n        addAction(\n          opts.telemetryActionName,\n          info as unknown as Record<string, unknown>,\n          opts.telemetrySampleRate\n        );\n      } catch {\n        // swallow\n      }\n    }\n  };\n\n  if (!opts.enabled) {\n    // Fast path: no Profiler wrapper if disabled\n    return <>{children}</>;\n  }\n\n  const RP: any = ReactProfiler as any;\n  return (\n    <RP id={id} onRender={__onRenderSpy || onRender}>\n      {children}\n    </RP>\n  );\n}\n\n/* ----------------------------- helpers ----------------------------- */\n\nfunction noop() {}\nfunction round2(n: number) {\n  return Math.round(n * 100) / 100;\n}\nfunction clampPct(n: number) {\n  if (Number.isNaN(n)) return 0;\n  return Math.max(0, Math.min(100, Math.round(n)));\n}\nfunction passSample(pct: number) {\n  return Math.random() * 100 < pct;\n}\n\n/* ------------------------------------------------------------------ */\n/**\n * ## Additional Examples\n *\n * ### 1) Detect chatty lists (development-only default)\n * ```tsx\n * <RenderDetector id=\"LargeList\">\n *   <LargeList items={items} />\n * </RenderDetector>\n * ```\n *\n * ### 2) Tighten thresholds for a known hot area\n * ```tsx\n * <RenderDetector\n *   id=\"AutoComplete\"\n *   options={{\n *     commitsPerSecThreshold: 8,     // allow more commits/sec\n *     renderMsPerSecThreshold: 16,   // stricter time budget\n *     minCommits: 5,\n *     cooldownMs: 3000,\n *     onHotspot: ({ commitsPerSec, renderMsPerSec }) => {\n *       console.warn(\"AutoComplete hotspot\", { commitsPerSec, renderMsPerSec });\n *     },\n *   }}\n * >\n *   <AutoComplete />\n * </RenderDetector>\n * ```\n *\n * ### 3) Disable telemetry but keep local warnings\n * ```tsx\n * <RenderDetector\n *   id=\"Chart\"\n *   options={{\n *     telemetrySampleRate: 0, // disable DataDog sends\n *     onHotspot: info => console.table(info),\n *   }}\n * >\n *   <Chart />\n * </RenderDetector>\n * ```\n *\n * ### 4) Add static context fields for grouping\n * ```tsx\n * <RenderDetector\n *   id=\"DashboardCards\"\n *   options={{ context: { route: \"/dashboard\", area: \"cards\" } }}\n * >\n *   <DashboardCards />\n * </RenderDetector>\n * ```\n */\n","/**\n * @file RenderProfiler.tsx\n * @description React Profiler wrapper that reports slow renders to Datadog RUM.\n */\nimport { Profiler, ProfilerOnRenderCallback, ReactNode } from \"react\";\nimport { datadogRum } from \"@datadog/browser-rum\";\nimport { getUxConfig } from \"../config.ts\";\n\ntype Props = {\n  id: string;\n  children: ReactNode;\n};\n\n/**\n * React Profiler wrapper that reports slow renders to Datadog RUM.\n * @param id - Unique identifier for the profiler.\n * @param children - Child React nodes to be profiled.\n * @returns The profiled React children.\n */\nexport const RenderProfiler = ({ id, children }: Props) => {\n  const cfg = getUxConfig();\n\n  const onRender: ProfilerOnRenderCallback = (\n    _id,\n    _phase,\n    actualDuration,\n    baseDuration,\n    startTime,\n    commitTime\n  ) => {\n    if (actualDuration >= cfg.renderSlowMs) {\n      datadogRum.addAction(\"render_slow\", {\n        id,\n        actual_ms: Math.round(actualDuration),\n        base_ms: Math.round(baseDuration),\n        started_at: Math.round(startTime),\n        committed_at: Math.round(commitTime),\n        threshold_ms: cfg.renderSlowMs,\n      });\n    }\n  };\n\n  return (\n    <Profiler id={id} onRender={onRender}>\n      {children}\n    </Profiler>\n  );\n};\n","/**\n * @file suspenseWatch.tsx\n * @description React Suspense boundary wrapper that reports slow fallbacks and resolutions to telemetry.\n */\nimport { ReactNode, useEffect, useRef, Suspense } from \"react\";\nimport { addAction, addError } from \"../datadog.ts\";\n\n/**\n * Options that control how SuspenseWatch reports and samples events.\n */\nexport type SuspenseWatchOptions = {\n  /**\n   * Milliseconds the fallback may remain visible before it's considered \"slow\".\n   * @default 1200\n   */\n  timeoutMs?: number;\n\n  /**\n   * % sample rate for telemetry (0–100). Set to 0 to disable Datadog reporting.\n   * @default 20\n   */\n  sampleRate?: number;\n\n  /**\n   * Emit a second event when content finally resolves after a slow fallback.\n   * @default true\n   */\n  reportResolveAfterSlow?: boolean;\n\n  /**\n   * Action names for Datadog. Usually you won't need to change these.\n   */\n  actionNames?: {\n    /** Emitted once when the fallback crosses timeoutMs. */\n    slow?: string; // default \"suspense_slow\"\n    /** Emitted when a slow fallback eventually resolves. */\n    resolved?: string; // default \"suspense_resolved_after_slow\"\n  };\n\n  /**\n   * Optional callback when the fallback crosses timeoutMs.\n   * Receives timing info; return value is ignored.\n   */\n  onSlow?: (info: SuspenseSlowInfo) => void;\n\n  /**\n   * Optional callback when the slow fallback eventually resolves.\n   */\n  onResolvedAfterSlow?: (info: SuspenseResolvedInfo) => void;\n\n  /**\n   * If true, also send a Datadog error on slow fallback (grouped as error).\n   * Useful when prolonged loading is considered a defect.\n   * @default false\n   */\n  alsoReportError?: boolean;\n};\n\n/**\n * Props for SuspenseWatch.\n */\nexport type SuspenseWatchProps = {\n  /**\n   * Human-friendly identifier for this boundary (e.g., \"SearchResults\", \"PatientCard\").\n   */\n  id: string;\n\n  /**\n   * The content that may suspend.\n   */\n  children: ReactNode;\n\n  /**\n   * The fallback UI to render while suspended.\n   */\n  fallback: ReactNode;\n\n  /**\n   * Reporting / behavior options.\n   */\n  options?: SuspenseWatchOptions;\n};\n\n/**\n * Info passed to `onSlow`.\n */\nexport type SuspenseSlowInfo = {\n  /** Boundary ID. */\n  id: string;\n  /** Timeout threshold in ms. */\n  timeoutMs: number;\n  /** When the fallback mounted (ms since page load). */\n  fallbackStart: number;\n};\n\n/**\n * Info passed to `onResolvedAfterSlow`.\n */\nexport type SuspenseResolvedInfo = SuspenseSlowInfo & {\n  /** How long the fallback was visible (ms). */\n  fallbackVisibleMs: number;\n};\n\n/**\n * SuspenseWatch\n * -------------\n * Wraps a React.Suspense boundary and reports when the fallback remains visible\n * longer than `timeoutMs`. A \"slow\" event is emitted once per suspend cycle\n * (even if the fallback remains longer), and an optional \"resolved\" event is\n * emitted when the content finally appears.\n *\n * Implementation details:\n * - We insert tiny sentinels inside both the fallback and the content tree.\n *   Mount/unmount of those sentinels tell us when the boundary is suspended\n *   or has resolved.\n * - Each time the boundary re-suspends, a new cycle begins and the logic repeats.\n * - Telemetry uses `addAction` (and optionally `addError`) and is sampled.\n *\n * @example\n * ```tsx\n * import { SuspenseWatch } from \"@milliman/datadog-ux-utils/react/SuspenseWatch\";\n *\n * export function ResultsSection() {\n *   return (\n *     <SuspenseWatch\n *       id=\"ResultsSection\"\n *       fallback={<Spinner label=\"Loading results…\" />}\n *       options={{\n *         timeoutMs: 1500,\n *         sampleRate: 25,\n *         onSlow: ({ id, timeoutMs }) => {\n *           console.warn(`${id} still loading after ${timeoutMs}ms`);\n *         },\n *       }}\n *     >\n *       <ResultsList />\n *     </SuspenseWatch>\n *   );\n * }\n * ```\n */\nexport function SuspenseWatch({\n  id,\n  children,\n  fallback,\n  options,\n}: SuspenseWatchProps) {\n  const opts = useRef<Required<SuspenseWatchOptions>>({\n    timeoutMs: options?.timeoutMs ?? 1200,\n    sampleRate: clampPct(options?.sampleRate ?? 20),\n    reportResolveAfterSlow: options?.reportResolveAfterSlow ?? true,\n    actionNames: {\n      slow: options?.actionNames?.slow ?? \"suspense_slow\",\n      resolved:\n        options?.actionNames?.resolved ?? \"suspense_resolved_after_slow\",\n    },\n    onSlow: options?.onSlow ?? noop,\n    onResolvedAfterSlow: options?.onResolvedAfterSlow ?? noop,\n    alsoReportError: options?.alsoReportError ?? false,\n  });\n\n  // Keep latest options without re-mounting sentinels\n  useEffect(() => {\n    opts.current = {\n      ...opts.current,\n      timeoutMs: options?.timeoutMs ?? opts.current.timeoutMs,\n      sampleRate: clampPct(options?.sampleRate ?? opts.current.sampleRate),\n      reportResolveAfterSlow:\n        options?.reportResolveAfterSlow ?? opts.current.reportResolveAfterSlow,\n      actionNames: {\n        slow: options?.actionNames?.slow ?? opts.current.actionNames.slow,\n        resolved:\n          options?.actionNames?.resolved ?? opts.current.actionNames.resolved,\n      },\n      onSlow: options?.onSlow ?? opts.current.onSlow,\n      onResolvedAfterSlow:\n        options?.onResolvedAfterSlow ?? opts.current.onResolvedAfterSlow,\n      alsoReportError: options?.alsoReportError ?? opts.current.alsoReportError,\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [options]);\n\n  // Per-cycle state (a “cycle” is fallback mount → content mount)\n  const cycleRef = useRef<{\n    fallbackStart: number;\n    slowEmitted: boolean;\n    slowTimer: number | null;\n  } | null>(null);\n\n  /** Called when fallback first becomes visible (suspension starts). */\n  const onFallbackMount = () => {\n    const { timeoutMs } = opts.current;\n    const start = performance.now();\n\n    // Begin a new cycle (in case of re-suspends)\n    endCycle(false); // clear previous\n    cycleRef.current = {\n      fallbackStart: start,\n      slowEmitted: false,\n      slowTimer: null,\n    };\n\n    // Schedule slow mark\n    const slowTimer = window.setTimeout(() => {\n      emitSlow(start);\n    }, timeoutMs);\n    cycleRef.current.slowTimer = slowTimer;\n  };\n\n  /** Called when fallback unmounts (content resolved). */\n  const onFallbackUnmount = () => {\n    // Nothing here; we’ll handle in content mount\n  };\n\n  /** Called when content finally mounts (resolution). */\n  const onContentMount = () => {\n    const c = cycleRef.current;\n    if (!c) return; // no active cycle (shouldn't happen, but safe)\n    // If slow already emitted and we want to report resolution, do so.\n    if (c.slowEmitted && opts.current.reportResolveAfterSlow) {\n      const duration = Math.round(performance.now() - c.fallbackStart);\n      const info: SuspenseResolvedInfo = {\n        id,\n        timeoutMs: opts.current.timeoutMs,\n        fallbackStart: c.fallbackStart,\n        fallbackVisibleMs: duration,\n      };\n      try {\n        opts.current.onResolvedAfterSlow(info);\n      } catch {\n        // intentionally ignore\n      }\n      if (opts.current.sampleRate > 0 && passSample(opts.current.sampleRate)) {\n        try {\n          addAction(\n            opts.current.actionNames.resolved || \"suspense_resolved_after_slow\",\n            info as any,\n            opts.current.sampleRate\n          );\n        } catch {\n          // intentionally ignore\n        }\n      }\n    }\n    // End the cycle and clear timers\n    endCycle(true);\n  };\n\n  /** Called when content unmounts (e.g., route away); clear timers. */\n  const onContentUnmount = () => {\n    endCycle(false);\n  };\n\n  /** Emit a single “slow” event for the current cycle. */\n  function emitSlow(fallbackStart: number) {\n    const c = cycleRef.current;\n    if (!c || c.slowEmitted) return;\n    c.slowEmitted = true;\n\n    const info: SuspenseSlowInfo = {\n      id,\n      timeoutMs: opts.current.timeoutMs,\n      fallbackStart,\n    };\n\n    // Local callback first\n    try {\n      opts.current.onSlow(info);\n    } catch {\n      // intentionally ignore\n    }\n\n    // Telemetry\n    if (opts.current.sampleRate > 0 && passSample(opts.current.sampleRate)) {\n      try {\n        addAction(\n          opts.current.actionNames.slow || \"suspense_slow\",\n          info as any,\n          opts.current.sampleRate\n        );\n      } catch {\n        // intentionally ignore\n      }\n      if (opts.current.alsoReportError) {\n        try {\n          addError(\n            new Error(`[suspense] ${id} exceeded ${opts.current.timeoutMs}ms`),\n            info as any,\n            opts.current.sampleRate\n          );\n        } catch {\n          // intentionally ignore\n        }\n      }\n    }\n  }\n\n  /** Clear timers and close the current cycle. */\n  function endCycle(clearRef: boolean) {\n    const c = cycleRef.current;\n    if (!c) return;\n    if (c.slowTimer != null) {\n      clearTimeout(c.slowTimer);\n      c.slowTimer = null;\n    }\n    if (clearRef) cycleRef.current = null;\n  }\n\n  // Cast Suspense to any to avoid TS JSX component constraint mismatch under current TS + React 19 types.\n  const S: any = Suspense as any;\n  return (\n    <S\n      fallback={\n        <FallbackSentinel\n          onMount={onFallbackMount}\n          onUnmount={onFallbackUnmount}\n        >\n          {fallback}\n        </FallbackSentinel>\n      }\n    >\n      <ContentSentinel onMount={onContentMount} onUnmount={onContentUnmount}>\n        {children}\n      </ContentSentinel>\n    </S>\n  );\n}\n\n/* ---------------------- sentinel components ---------------------- */\n\n/** Mounts while the Suspense fallback is visible. */\nfunction FallbackSentinel({\n  onMount,\n  onUnmount,\n  children,\n}: {\n  onMount: () => void;\n  onUnmount: () => void;\n  children: ReactNode;\n}) {\n  useEffect(() => {\n    onMount();\n    return onUnmount;\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n  return <>{children}</>;\n}\n\n/** Mounts when the Suspense content resolves. */\nfunction ContentSentinel({\n  onMount,\n  onUnmount,\n  children,\n}: {\n  onMount: () => void;\n  onUnmount: () => void;\n  children: ReactNode;\n}) {\n  useEffect(() => {\n    onMount();\n    return onUnmount;\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n  return <>{children}</>;\n}\n\n/* ------------------------------ utils ------------------------------ */\n\nfunction clampPct(n: number) {\n  if (Number.isNaN(n)) return 0;\n  return Math.max(0, Math.min(100, Math.round(n)));\n}\nfunction passSample(pct: number) {\n  return Math.random() * 100 < pct;\n}\nfunction noop() {}\n","/**\n * @file useGuardFetch.ts\n * @description React hook for guarded fetch calls with rate limiting and auto-retry support.\n */\nimport { useCallback, useMemo, useRef, useEffect } from \"react\";\nimport { ApiRateGuard, ApiRunawayBlockedError } from \"../api/rateGuard.ts\";\n\nexport type UseGuardedFetchOptions = {\n  onBlocked?: (info: {\n    key: string;\n    until: number;\n    windowMs: number;\n    maxRequests: number;\n  }) => void;\n\n  /**\n   * If 'wait', automatically retry once after the block window ends.\n   * If 'none', do not retry.\n   * Default: 'none'\n   */\n  retryAfter?: \"wait\" | \"none\";\n\n  /**\n   * Optional signal to cancel the auto-retry wait (e.g., route change)\n   */\n  cancelSignal?: AbortSignal;\n};\n\n/**\n * A thin wrapper that uses ApiRateGuard for fetch() calls and\n * gives you a single place to react to runaway bursts.\n *\n * Usage:\n *   const guardedFetch = useGuardedFetch(apiGuard, { onBlocked: showToast });\n *   const resp = await guardedFetch('/api/items');\n */\nexport function useGuardedFetch(\n  guard: ApiRateGuard,\n  opts?: UseGuardedFetchOptions\n) {\n  const retryAfter = opts?.retryAfter ?? \"none\";\n  const cancelRef = useRef<AbortSignal | null>(opts?.cancelSignal ?? null);\n\n  useEffect(() => {\n    cancelRef.current = opts?.cancelSignal ?? null;\n  }, [opts?.cancelSignal]);\n\n  const wait = useCallback(async (ms: number) => {\n    if (ms <= 0) return;\n    await new Promise<void>((resolve, reject) => {\n      const t = setTimeout(resolve, ms);\n      const sig = cancelRef.current;\n      const onAbort = () => {\n        clearTimeout(t);\n        reject(new DOMException(\"Aborted\", \"AbortError\"));\n      };\n      if (sig) {\n        if (sig.aborted) return onAbort();\n        sig.addEventListener(\"abort\", onAbort, { once: true });\n      }\n    });\n  }, []);\n\n  const guardedFetch = useCallback(\n    async (input: RequestInfo | URL, init?: RequestInit) => {\n      try {\n        return await guard.guardFetch(input, init);\n      } catch (e) {\n        if (e instanceof ApiRunawayBlockedError) {\n          opts?.onBlocked?.({\n            key: e.key,\n            until: e.until,\n            windowMs: e.windowMs,\n            maxRequests: e.maxRequests,\n          });\n\n          if (retryAfter === \"wait\") {\n            const delay = e.until - Date.now();\n            try {\n              await wait(delay);\n              // After the block, try once more (still guarded)\n              return await guard.guardFetch(input, init);\n            } catch (abortErr) {\n              // If aborted, surface a clean error or rethrow\n              throw abortErr;\n            }\n          }\n        }\n        throw e;\n      }\n    },\n    [guard, retryAfter, wait, opts]\n  );\n\n  return useMemo(() => guardedFetch, [guardedFetch]);\n}\n","/**\n * @file useGuardedCall.ts\n * @description React hook for guarded async calls with rate limiting and optional auto-retry.\n */\nimport { useCallback, useMemo } from \"react\";\nimport { ApiRateGuard, ApiRunawayBlockedError } from \"../api/rateGuard.ts\";\n\nexport type UseGuardedCallOptions = {\n  onBlocked?: (info: {\n    key: string;\n    until: number;\n    windowMs: number;\n    maxRequests: number;\n  }) => void;\n  retryAfter?: \"wait\" | \"none\";\n};\n\n/**\n * Guard any async call (Axios, graphql, SDK).\n *\n * Usage:\n *   const guardedCall = useGuardedCall(apiGuard, { onBlocked: toast });\n *   const data = await guardedCall('POST /api/orders', () => axios.post(...));\n */\nexport function useGuardedCall(\n  guard: ApiRateGuard,\n  opts?: UseGuardedCallOptions\n) {\n  const retryAfter = opts?.retryAfter ?? \"none\";\n\n  const fn = useCallback(\n    async <T>(key: string, call: () => Promise<T>) => {\n      try {\n        return await guard.guard(key, call);\n      } catch (e) {\n        if (e instanceof ApiRunawayBlockedError) {\n          opts?.onBlocked?.({\n            key: e.key,\n            until: e.until,\n            windowMs: e.windowMs,\n            maxRequests: e.maxRequests,\n          });\n\n          if (retryAfter === \"wait\") {\n            const delay = e.until - Date.now();\n            if (delay > 0) await new Promise((r) => setTimeout(r, delay));\n            return guard.guard(key, call);\n          }\n        }\n        throw e;\n      }\n    },\n    [guard, retryAfter, opts]\n  );\n\n  return useMemo(() => fn, [fn]);\n}\n","/**\n * @file useComponentTelemetry.tsx\n * @description React hook that reports the first mount of a component to the component telemetry queue.\n */\nimport { useEffect } from \"react\";\n// Explicitly referencing index to avoid any resolution ambiguity in some TS setups\nimport { reportComponentMount } from \"../telemetry/index.ts\";\n\n/**\n * @category Telemetry\n * React hook that reports the *first* mount of a component to the component telemetry queue.\n *\n * Usage:\n * ```tsx\n * import { useComponentTelemetry } from 'datadog-ux-utils/react';\n *\n * export function Button(props) {\n *   useComponentTelemetry('Button', { variant: props.variant });\n *   return <button {...props} />;\n * }\n * ```\n * Ensure you called `initComponentTelemetry()` somewhere during app startup.\n */\nexport function useComponentTelemetry(\n  componentName: string,\n  opts?: { variant?: string; route?: string; force?: boolean }\n) {\n  useEffect(() => {\n    reportComponentMount(componentName, opts);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n}\n"],"names":["ErrorBoundary","Component","error","info","name","datadogRum","getUxConfig","REACT_ELEMENT_TYPE","REACT_FRAGMENT_TYPE","jsxProd","type","config","maybeKey","key","propName","reactJsxRuntime_production","getComponentNameFromType","REACT_CLIENT_REFERENCE","REACT_PROFILER_TYPE","REACT_STRICT_MODE_TYPE","REACT_SUSPENSE_TYPE","REACT_SUSPENSE_LIST_TYPE","REACT_ACTIVITY_TYPE","REACT_PORTAL_TYPE","REACT_CONTEXT_TYPE","REACT_CONSUMER_TYPE","REACT_FORWARD_REF_TYPE","innerType","REACT_MEMO_TYPE","REACT_LAZY_TYPE","testStringCoercion","value","checkKeyStringCoercion","JSCompiler_inline_result","JSCompiler_temp_const","JSCompiler_inline_result$jscomp$0","getTaskName","getOwner","dispatcher","ReactSharedInternals","UnknownOwner","hasValidKey","hasOwnProperty","getter","defineKeyPropWarningGetter","props","displayName","warnAboutAccessingKey","specialPropKeyWarningShown","elementRefGetterWithDeprecationWarning","componentName","didWarnAboutElementRef","ReactElement","self","source","owner","debugStack","debugTask","jsxDEVImpl","isStaticChildren","children","isArrayImpl","validateChildKeys","keys","k","didWarnAboutKeySpread","node","React","require$$0","createTask","callStackForError","unknownOwnerDebugStack","unknownOwnerDebugTask","reactJsxRuntime_development","trackActualOwner","jsxRuntimeModule","require$$1","RenderDetector","id","options","__onRenderSpy","opts","useMemo","clampPct","noop","commitsRef","useRef","lastReportAtRef","onRender","_id","phase","actualDuration","baseDuration","startTime","_commitTime","now","windowStart","buf","c","commitsInWindow","durationSum","acc","seconds","commitsPerSec","renderMsPerSec","reasons","lastAt","last","round2","passSample","addAction","RP","ReactProfiler","n","pct","RenderProfiler","cfg","_phase","commitTime","jsx","Profiler","SuspenseWatch","fallback","useEffect","cycleRef","onFallbackMount","timeoutMs","start","endCycle","slowTimer","emitSlow","onFallbackUnmount","onContentMount","duration","onContentUnmount","fallbackStart","addError","clearRef","S","Suspense","FallbackSentinel","ContentSentinel","onMount","onUnmount","useGuardedFetch","guard","retryAfter","cancelRef","wait","useCallback","ms","resolve","reject","t","sig","onAbort","guardedFetch","input","init","e","ApiRunawayBlockedError","delay","abortErr","useGuardedCall","fn","call","r","useComponentTelemetry","reportComponentMount"],"mappings":"wOAmCO,MAAMA,WAAsBC,EAAAA,SAAwC,CAApE,aAAA,CAAA,MAAA,GAAA,SAAA,EACL,KAAA,MAAQ,CAAE,SAAU,EAAA,CAAM,CAK1B,OAAO,0BAA2B,CAChC,MAAO,CAAE,SAAU,EAAA,CACrB,CAOA,kBAAkBC,EAAcC,EAAiB,CAC/C,KAAM,CAAE,KAAAC,GAAS,KAAK,MACtBC,GAAAA,WAAW,SAASH,EAAO,CACzB,SAAUE,GAAQ,gBAClB,eAAgBD,EAAK,eACrB,IAAKG,GAAAA,cAAc,OAAA,CACpB,CACH,CAKA,QAAS,CACP,OAAI,KAAK,MAAM,SAAiB,KAAK,MAAM,UAAY,KAChD,KAAK,MAAM,QACpB,CACF;;;;;;;;yCCvDA,IAAIC,EAAqB,OAAO,IAAI,4BAA4B,EAC9DC,EAAsB,OAAO,IAAI,gBAAgB,EACnD,SAASC,EAAQC,EAAMC,EAAQC,EAAU,CACvC,IAAIC,EAAM,KAGV,GAFWD,IAAX,SAAwBC,EAAM,GAAKD,GACxBD,EAAO,MAAlB,SAA0BE,EAAM,GAAKF,EAAO,KACxC,QAASA,EAAQ,CACnBC,EAAW,CAAA,EACX,QAASE,K