UNPKG

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
{"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