react-render-pulse 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -2
  3. package/dist/aggregate/aggregator.d.cts +27 -0
  4. package/dist/aggregate/aggregator.d.ts +28 -0
  5. package/dist/aggregate/aggregator.d.ts.map +1 -0
  6. package/dist/aggregate/histogram.d.cts +18 -0
  7. package/dist/aggregate/histogram.d.ts +19 -0
  8. package/dist/aggregate/histogram.d.ts.map +1 -0
  9. package/dist/components/RenderMonitor.d.cts +19 -0
  10. package/dist/components/RenderMonitor.d.ts +20 -0
  11. package/dist/components/RenderMonitor.d.ts.map +1 -0
  12. package/dist/context/device.d.cts +2 -0
  13. package/dist/context/device.d.ts +3 -0
  14. package/dist/context/device.d.ts.map +1 -0
  15. package/dist/context/route.d.cts +7 -0
  16. package/dist/context/route.d.ts +8 -0
  17. package/dist/context/route.d.ts.map +1 -0
  18. package/dist/core.cjs +56 -0
  19. package/dist/core.cjs.map +1 -0
  20. package/dist/core.d.cts +2 -0
  21. package/dist/core.d.ts +3 -0
  22. package/dist/core.d.ts.map +1 -0
  23. package/dist/core.js +28 -0
  24. package/dist/core.js.map +1 -0
  25. package/dist/export/console.d.cts +3 -0
  26. package/dist/export/console.d.ts +4 -0
  27. package/dist/export/console.d.ts.map +1 -0
  28. package/dist/export/otel.d.cts +34 -0
  29. package/dist/export/otel.d.ts +35 -0
  30. package/dist/export/otel.d.ts.map +1 -0
  31. package/dist/hooks/useRenderMonitor.d.cts +9 -0
  32. package/dist/hooks/useRenderMonitor.d.ts +10 -0
  33. package/dist/hooks/useRenderMonitor.d.ts.map +1 -0
  34. package/dist/index.cjs +634 -0
  35. package/dist/index.cjs.map +1 -0
  36. package/dist/index.d.cts +8 -0
  37. package/dist/index.d.ts +9 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +602 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/modes/detect.d.cts +16 -0
  42. package/dist/modes/detect.d.ts +17 -0
  43. package/dist/modes/detect.d.ts.map +1 -0
  44. package/dist/observe/longtasks.d.cts +12 -0
  45. package/dist/observe/longtasks.d.ts +13 -0
  46. package/dist/observe/longtasks.d.ts.map +1 -0
  47. package/dist/observe/renderWindows.d.cts +17 -0
  48. package/dist/observe/renderWindows.d.ts +18 -0
  49. package/dist/observe/renderWindows.d.ts.map +1 -0
  50. package/dist/otel.cjs +65 -0
  51. package/dist/otel.cjs.map +1 -0
  52. package/dist/otel.d.cts +2 -0
  53. package/dist/otel.d.ts +3 -0
  54. package/dist/otel.d.ts.map +1 -0
  55. package/dist/otel.js +38 -0
  56. package/dist/otel.js.map +1 -0
  57. package/dist/runtime/guard.d.cts +7 -0
  58. package/dist/runtime/guard.d.ts +8 -0
  59. package/dist/runtime/guard.d.ts.map +1 -0
  60. package/dist/runtime/initRenderPulse.d.cts +44 -0
  61. package/dist/runtime/initRenderPulse.d.ts +45 -0
  62. package/dist/runtime/initRenderPulse.d.ts.map +1 -0
  63. package/dist/runtime/runtime.d.cts +13 -0
  64. package/dist/runtime/runtime.d.ts +14 -0
  65. package/dist/runtime/runtime.d.ts.map +1 -0
  66. package/dist/runtime/useIsomorphicLayoutEffect.d.cts +2 -0
  67. package/dist/runtime/useIsomorphicLayoutEffect.d.ts +3 -0
  68. package/dist/runtime/useIsomorphicLayoutEffect.d.ts.map +1 -0
  69. package/dist/sample/sample.d.cts +7 -0
  70. package/dist/sample/sample.d.ts +8 -0
  71. package/dist/sample/sample.d.ts.map +1 -0
  72. package/dist/transport/payload.d.cts +8 -0
  73. package/dist/transport/payload.d.ts +9 -0
  74. package/dist/transport/payload.d.ts.map +1 -0
  75. package/dist/transport/transport.d.cts +18 -0
  76. package/dist/transport/transport.d.ts +19 -0
  77. package/dist/transport/transport.d.ts.map +1 -0
  78. package/dist/types.d.cts +120 -0
  79. package/dist/types.d.ts +121 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/package.json +128 -3
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/aggregate/histogram.ts","../src/aggregate/aggregator.ts","../src/context/device.ts","../src/context/route.ts","../src/export/console.ts","../src/modes/detect.ts","../src/observe/longtasks.ts","../src/observe/renderWindows.ts","../src/sample/sample.ts","../src/transport/payload.ts","../src/transport/transport.ts","../src/runtime/guard.ts","../src/runtime/runtime.ts","../src/runtime/initRenderPulse.ts","../src/components/RenderMonitor.tsx","../src/runtime/useIsomorphicLayoutEffect.ts","../src/hooks/useRenderMonitor.ts"],"sourcesContent":["/**\n * Upper bounds (ms, inclusive) of the render-duration histogram buckets, plus\n * an implicit overflow bucket. Part of payload v1: the collector merges\n * histograms by index, so changing these needs a new payload version.\n */\nexport const HISTOGRAM_BOUNDS = [1, 2, 4, 8, 12, 16, 24, 33, 50, 75, 100, 150, 250, 500, 1000] as const;\n\nexport function createHistogram(): number[] {\n return new Array<number>(HISTOGRAM_BOUNDS.length + 1).fill(0);\n}\n\nexport function bucketIndex(ms: number): number {\n for (let i = 0; i < HISTOGRAM_BOUNDS.length; i++) {\n if (ms <= (HISTOGRAM_BOUNDS[i] ?? 0)) return i;\n }\n return HISTOGRAM_BOUNDS.length;\n}\n\n/**\n * Estimates a percentile (0–100) from bucket counts, interpolating linearly\n * inside the bucket. The observed `min` and `max` narrow the bucket's range\n * (every value lies between them): without them, durations clustered at a\n * bucket edge, like many renders of exactly 8 ms, would be estimated from\n * the bucket's middle instead.\n */\nexport function histogramPercentile(hist: readonly number[], p: number, max: number, min = 0): number {\n const total = hist.reduce((sum, n) => sum + n, 0);\n if (total === 0) return 0;\n\n const target = Math.max((p / 100) * total, Number.MIN_VALUE);\n let seen = 0;\n\n for (let i = 0; i < hist.length; i++) {\n const count = hist[i] ?? 0;\n if (count === 0) continue;\n\n if (seen + count >= target) {\n const upper = Math.min(HISTOGRAM_BOUNDS[i] ?? max, max);\n const lower = Math.min(Math.max(i === 0 ? 0 : (HISTOGRAM_BOUNDS[i - 1] ?? 0), min), upper);\n const fraction = (target - seen) / count;\n return round(Math.min(lower + (upper - lower) * fraction, max));\n }\n\n seen += count;\n }\n\n return round(max);\n}\n\n/** Rounds to 0.1 ms: finer precision isn't meaningful and costs payload bytes. */\nexport function round(ms: number): number {\n return Math.round(ms * 10) / 10;\n}\n","import { bucketIndex, createHistogram, histogramPercentile, round } from \"./histogram.js\";\nimport type { AreaReport, LongTaskEvent, LongTaskReport, RenderEvent, SessionSummary, WorstCase } from \"../types.js\";\n\nexport interface AggregatorOptions {\n /** Renders slower than this count as slow. */\n slowRenderMs: number;\n /** Most distinct area/route pairs kept per batch; renders for new ones past this are dropped. */\n maxAreas: number;\n /** How many of the slowest renders to keep per area. */\n worstCases: number;\n /** `performance.now()` at session start, so worst cases report time since then. */\n sessionStart: number;\n}\n\nexport interface AggregatedBatch {\n summary: SessionSummary;\n areas: AreaReport[];\n longTasks: LongTaskReport[];\n longFrames: LongTaskReport[];\n droppedAreas: number;\n}\n\nexport interface Aggregator {\n addRender: (event: RenderEvent) => void;\n addLongTask: (event: LongTaskEvent) => void;\n /** True when nothing was recorded since the last `drain()`. */\n isEmpty: () => boolean;\n /** Returns everything recorded since the last call, and starts a new batch. */\n drain: () => AggregatedBatch;\n}\n\ninterface AreaAccumulator {\n id: string;\n route: string;\n renders: number;\n mounts: number;\n slow: number;\n totalMs: number;\n min: number;\n max: number;\n hist: number[];\n worst: WorstCase[];\n}\n\ninterface LongTaskAccumulator {\n area: string | null;\n route: string;\n count: number;\n totalMs: number;\n maxMs: number;\n}\n\n// Only counts, histograms, and a few worst cases are kept, never raw event\n// streams, so memory stays flat however long the session runs.\nexport function createAggregator(options: AggregatorOptions): Aggregator {\n let areas = new Map<string, AreaAccumulator>();\n let longTasks = new Map<string, LongTaskAccumulator>();\n let longFrames = new Map<string, LongTaskAccumulator>();\n let droppedAreas = 0;\n\n function addRender(event: RenderEvent): void {\n const key = `${event.route}\\u0000${event.area}`;\n let area = areas.get(key);\n\n if (!area) {\n if (areas.size >= options.maxAreas) {\n droppedAreas++;\n return;\n }\n area = {\n id: event.area,\n route: event.route,\n renders: 0,\n mounts: 0,\n slow: 0,\n totalMs: 0,\n min: Infinity,\n max: 0,\n hist: createHistogram(),\n worst: [],\n };\n areas.set(key, area);\n }\n\n const ms = event.duration;\n area.renders++;\n if (event.phase === \"mount\") area.mounts++;\n if (ms > options.slowRenderMs) area.slow++;\n area.totalMs += ms;\n area.min = Math.min(area.min, ms);\n area.max = Math.max(area.max, ms);\n const bucket = bucketIndex(ms);\n area.hist[bucket] = (area.hist[bucket] ?? 0) + 1;\n addWorstCase(area.worst, { ms, phase: event.phase, at: event.startTime - options.sessionStart });\n }\n\n function addWorstCase(worst: WorstCase[], candidate: WorstCase): void {\n if (worst.length < options.worstCases) {\n worst.push(candidate);\n } else {\n const fastest = worst.reduce((min, w, i) => (w.ms < (worst[min]?.ms ?? 0) ? i : min), 0);\n if (candidate.ms <= (worst[fastest]?.ms ?? 0)) return;\n worst[fastest] = candidate;\n }\n }\n\n function addLongTask(event: LongTaskEvent): void {\n const target = event.kind === \"longtask\" ? longTasks : longFrames;\n const key = `${event.route}\\u0000${event.area ?? \"\"}`;\n let entry = target.get(key);\n\n if (!entry) {\n entry = { area: event.area, route: event.route, count: 0, totalMs: 0, maxMs: 0 };\n target.set(key, entry);\n }\n\n entry.count++;\n entry.totalMs += event.duration;\n entry.maxMs = Math.max(entry.maxMs, event.duration);\n }\n\n function drain(): AggregatedBatch {\n const areaReports = Array.from(areas.values(), toAreaReport);\n const taskReports = Array.from(longTasks.values(), toLongTaskReport);\n const frameReports = Array.from(longFrames.values(), toLongTaskReport);\n\n const batch: AggregatedBatch = {\n summary: {\n renders: sum(areaReports, (a) => a.renders),\n slowRenders: sum(areaReports, (a) => a.slow),\n longTasks: sum(taskReports, (t) => t.count),\n longTaskMs: round(sum(taskReports, (t) => t.totalMs)),\n longFrames: sum(frameReports, (t) => t.count),\n longFrameMs: round(sum(frameReports, (t) => t.totalMs)),\n },\n areas: areaReports,\n longTasks: taskReports,\n longFrames: frameReports,\n droppedAreas,\n };\n\n areas = new Map();\n longTasks = new Map();\n longFrames = new Map();\n droppedAreas = 0;\n return batch;\n }\n\n return {\n addRender,\n addLongTask,\n isEmpty: () => areas.size === 0 && longTasks.size === 0 && longFrames.size === 0 && droppedAreas === 0,\n drain,\n };\n}\n\nfunction toAreaReport(area: AreaAccumulator): AreaReport {\n return {\n id: area.id,\n route: area.route,\n renders: area.renders,\n mounts: area.mounts,\n slow: area.slow,\n totalMs: round(area.totalMs),\n min: round(area.min),\n max: round(area.max),\n p50: histogramPercentile(area.hist, 50, area.max, area.min),\n p75: histogramPercentile(area.hist, 75, area.max, area.min),\n p95: histogramPercentile(area.hist, 95, area.max, area.min),\n hist: area.hist,\n worst: area.worst\n .sort((a, b) => b.ms - a.ms)\n .map((w) => ({ ms: round(w.ms), phase: w.phase, at: Math.round(w.at) })),\n };\n}\n\nfunction toLongTaskReport(entry: LongTaskAccumulator): LongTaskReport {\n return { ...entry, totalMs: round(entry.totalMs), maxMs: round(entry.maxMs) };\n}\n\nfunction sum<T>(items: readonly T[], pick: (item: T) => number): number {\n return items.reduce((total, item) => total + pick(item), 0);\n}\n","import type { DeviceContext } from \"../types.js\";\n\n// Non-standard APIs (Chromium only), so they're read defensively.\ninterface NavigatorWithHints {\n deviceMemory?: number;\n hardwareConcurrency?: number;\n connection?: { effectiveType?: string };\n}\n\nexport function getDeviceContext(): DeviceContext {\n const nav = (typeof navigator === \"undefined\" ? {} : navigator) as NavigatorWithHints;\n\n return {\n memoryGb: typeof nav.deviceMemory === \"number\" ? nav.deviceMemory : null,\n cores: typeof nav.hardwareConcurrency === \"number\" ? nav.hardwareConcurrency : null,\n connection: nav.connection?.effectiveType ?? null,\n viewport: typeof window === \"undefined\" ? \"0x0\" : `${window.innerWidth}x${window.innerHeight}`,\n };\n}\n","/** Default route reader: the path with numeric segments replaced, so `/orders/42` and `/orders/7` group together. */\nexport function defaultGetRoute(): string {\n if (typeof location === \"undefined\") return \"/\";\n return location.pathname.replace(/\\/\\d+(?=\\/|$)/g, \"/:id\");\n}\n\n/**\n * Wraps a route reader so it only runs when the URL changes, since it's\n * called on every measured render.\n */\nexport function createRouteReader(getRoute: () => string): () => string {\n let lastHref: string | null = null;\n let lastRoute = \"/\";\n\n return () => {\n const href = typeof location === \"undefined\" ? \"\" : location.href;\n if (href !== lastHref) {\n lastHref = href;\n lastRoute = getRoute();\n }\n return lastRoute;\n };\n}\n","import type { Exporter } from \"../types.js\";\n\n/** Logs each batch as a table, slowest areas first. Used by default when no `endpoint` is set, for local testing. */\nexport function createConsoleExporter(): Exporter {\n return {\n onFlush(payload) {\n if (typeof console === \"undefined\") return;\n\n const rows = [...payload.areas]\n .sort((a, b) => b.p75 - a.p75)\n .map((a) => ({\n area: a.id,\n route: a.route,\n renders: a.renders,\n slow: a.slow,\n p50: a.p50,\n p75: a.p75,\n p95: a.p95,\n max: a.max,\n }));\n\n console.groupCollapsed(\n `[render-pulse] batch ${payload.session.seq} (${payload.session.mode}): ${payload.summary.renders} renders, ${payload.summary.longTasks} long tasks`,\n );\n if (rows.length > 0) console.table(rows);\n if (payload.longTasks.length > 0) console.table(payload.longTasks);\n if (payload.longFrames.length > 0) console.table(payload.longFrames);\n console.groupEnd();\n },\n };\n}\n","import type { Mode, ModeOption } from \"../types.js\";\n\nexport interface ModeDetector {\n /** The mode measurements are currently taken in. */\n current: () => Mode;\n /** True when the performance-mark measurements in `<RenderMonitor>` should run. */\n measuresWithMarks: () => boolean;\n /** Called on every `<Profiler>` callback; in `\"auto\"` mode the first one switches to profiling mode. */\n profilerFired: () => void;\n}\n\n/**\n * React only calls `<Profiler onRender>` in development and in its profiling\n * build, so a callback arriving is proof that per-subtree timings are\n * available. There's no public API to ask which build is loaded, and this\n * avoids relying on React internals.\n */\nexport function createModeDetector(option: ModeOption): ModeDetector {\n let mode: Mode = option === \"profiling\" ? \"profiling\" : \"lightweight\";\n\n return {\n current: () => mode,\n measuresWithMarks: () => mode === \"lightweight\",\n profilerFired() {\n if (option === \"auto\") mode = \"profiling\";\n },\n };\n}\n","import type { LongTaskKind } from \"../types.js\";\n\nexport interface ObservedLongTask {\n kind: LongTaskKind;\n startTime: number;\n duration: number;\n}\n\n/**\n * Observes long tasks (Chromium) and long animation frames (Chromium 123+)\n * at or above `thresholdMs`. Entry types the browser doesn't support are\n * skipped. Returns a function that stops observing.\n */\nexport function observeLongTasks(thresholdMs: number, onEntry: (task: ObservedLongTask) => void): () => void {\n if (typeof PerformanceObserver === \"undefined\") return () => {};\n\n const supported = PerformanceObserver.supportedEntryTypes ?? [];\n const observers: PerformanceObserver[] = [];\n\n for (const kind of [\"longtask\", \"long-animation-frame\"] as const) {\n if (!supported.includes(kind)) continue;\n\n try {\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.duration < thresholdMs) continue;\n onEntry({ kind, startTime: entry.startTime, duration: entry.duration });\n }\n });\n observer.observe({ type: kind, buffered: true });\n observers.push(observer);\n } catch {\n // Observing is best-effort; a browser quirk here must not reach the app.\n }\n }\n\n return () => {\n for (const observer of observers) observer.disconnect();\n };\n}\n","interface RenderWindow {\n area: string;\n start: number;\n end: number;\n duration: number;\n}\n\nexport interface RenderWindows {\n /** Records that `area` rendered for `duration` ms somewhere between `start` and `end`. */\n add: (area: string, start: number, end: number, duration: number) => void;\n /** The area to blame for a main-thread block over `[start, end]`, or `null` when no recent render overlaps it. */\n findArea: (start: number, end: number) => string | null;\n}\n\n/**\n * A fixed-size ring of recent render windows. Long-task entries arrive a\n * little after the fact, so attribution looks back over the last `size` renders.\n *\n * Nested areas overlap the same block, and an outer area's duration includes\n * its inner ones. So the blame goes to the innermost overlapping area that\n * still accounts for at least half of the longest overlapping render: an\n * inner area that's responsible for most of the time, rather than always the\n * outermost wrapper or a tiny sibling that happened to render in the same commit.\n */\nexport function createRenderWindows(size = 200): RenderWindows {\n const windows: (RenderWindow | undefined)[] = new Array(size);\n let next = 0;\n\n return {\n add(area, start, end, duration) {\n windows[next] = { area, start, end, duration };\n next = (next + 1) % size;\n },\n findArea(start, end) {\n const overlapping = windows.filter((w): w is RenderWindow => w !== undefined && w.end >= start && w.start <= end);\n if (overlapping.length === 0) return null;\n\n const longest = Math.max(...overlapping.map((w) => w.duration));\n let best: RenderWindow | null = null;\n\n for (const window of overlapping) {\n if (window.duration < longest / 2) continue;\n if (!best || window.duration < best.duration) best = window;\n }\n\n return best?.area ?? null;\n },\n };\n}\n","/**\n * Decides once per session whether to collect, so unsampled sessions pay\n * almost nothing. `sampleRate` is clamped to 0–1.\n */\nexport function shouldSample(sampleRate: number, random: () => number = Math.random): boolean {\n if (!(sampleRate > 0)) return false;\n if (sampleRate >= 1) return true;\n return random() < sampleRate;\n}\n\n/** A random session id. Not derived from anything about the user, and never persisted. */\nexport function createSessionId(): string {\n try {\n // randomUUID only exists in secure contexts (HTTPS, localhost).\n const cryptoApi = globalThis.crypto as Crypto | undefined;\n if (cryptoApi && typeof cryptoApi.randomUUID === \"function\") return cryptoApi.randomUUID();\n } catch {\n // Fall through to the non-crypto id.\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n","import type { Payload } from \"../types.js\";\n\nexport function byteLength(text: string): number {\n return typeof TextEncoder === \"undefined\" ? text.length * 3 : new TextEncoder().encode(text).length;\n}\n\n/**\n * Serializes a payload, shrinking it until it fits in `maxBytes`: first the\n * worst-case lists go, then the areas with the fewest renders. Returns `null`\n * when even an empty payload doesn't fit.\n */\nexport function serializePayload(payload: Payload, maxBytes: number): string | null {\n let json = JSON.stringify(payload);\n if (byteLength(json) <= maxBytes) return json;\n\n let trimmed: Payload = { ...payload, areas: payload.areas.map((a) => ({ ...a, worst: [] })) };\n json = JSON.stringify(trimmed);\n\n if (byteLength(json) > maxBytes) {\n const areas = [...trimmed.areas].sort((a, b) => b.renders - a.renders);\n let dropped = trimmed.dropped.areas;\n\n // Halve the kept areas until it fits, then the list is short enough to stop.\n while (areas.length > 0 && byteLength(json) > maxBytes) {\n const remove = Math.max(1, Math.floor(areas.length / 2));\n areas.splice(areas.length - remove, remove);\n dropped += remove;\n trimmed = { ...trimmed, areas: [...areas], dropped: { areas: dropped } };\n json = JSON.stringify(trimmed);\n }\n }\n\n return byteLength(json) <= maxBytes ? json : null;\n}\n","export interface TransportOptions {\n endpoint: string;\n /** Failed sends are retried on later flushes, up to this many attempts in total. */\n maxAttempts: number;\n /** Most failed bodies kept for retry; the oldest are dropped past this. */\n maxQueued: number;\n}\n\nexport interface Transport {\n /**\n * Sends a serialized payload. With `unloading`, uses `navigator.sendBeacon`\n * (falling back to `fetch` with `keepalive`), since the page may be gone\n * before a normal request finishes. Never throws or rejects.\n */\n send: (body: string, options: { unloading: boolean }) => void;\n}\n\ninterface QueuedBody {\n body: string;\n attempts: number;\n}\n\nexport function createTransport(options: TransportOptions): Transport {\n const queue: QueuedBody[] = [];\n\n function enqueue(item: QueuedBody): void {\n if (item.attempts >= options.maxAttempts) return;\n queue.push(item);\n if (queue.length > options.maxQueued) queue.shift();\n }\n\n function beacon(body: string): boolean {\n try {\n // A string body is sent as text/plain, a CORS-safelisted type, so\n // cross-origin collectors don't need a preflight the beacon can't do.\n return typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\"\n ? navigator.sendBeacon(options.endpoint, body)\n : false;\n } catch {\n return false;\n }\n }\n\n function post(item: QueuedBody): void {\n if (typeof fetch !== \"function\") return;\n const attempt = { body: item.body, attempts: item.attempts + 1 };\n\n try {\n fetch(options.endpoint, { method: \"POST\", body: item.body, keepalive: true })\n .then((response) => {\n // 4xx means the collector rejected the payload; retrying won't help.\n if (response.status >= 500 || response.status === 429) enqueue(attempt);\n })\n .catch(() => enqueue(attempt));\n } catch {\n enqueue(attempt);\n }\n }\n\n return {\n send(body, { unloading }) {\n const items = [...queue.splice(0), { body, attempts: 0 }];\n\n for (const item of items) {\n if (unloading && beacon(item.body)) continue;\n post(item);\n }\n },\n };\n}\n","let debug = false;\n\nexport function setDebug(value: boolean): void {\n debug = value;\n}\n\n/**\n * Runs `fn`, swallowing anything it throws, so an error inside render-pulse\n * (or a user-supplied exporter or `getRoute`) never reaches the app. With\n * `debug`, the error is logged instead of silently dropped.\n */\nexport function guard<T>(fn: () => T, fallback: T): T {\n try {\n return fn();\n } catch (error) {\n if (debug && typeof console !== \"undefined\") console.warn(\"[render-pulse]\", error);\n return fallback;\n }\n}\n","import type { RenderPhase } from \"../types.js\";\n\n/** What `<RenderMonitor>` and `useRenderMonitor` call into. Every method is safe to call and never throws. */\nexport interface PulseRuntime {\n readonly sampled: boolean;\n /** True when `<RenderMonitor>` should take performance-mark measurements (lightweight mode). */\n measuresWithMarks: () => boolean;\n now: () => number;\n recordMarks: (area: string, phase: RenderPhase, start: number, end: number) => void;\n /** Returns true when the measurement was recorded, i.e. the session is in profiling mode. */\n recordProfiler: (\n area: string,\n phase: RenderPhase,\n actualDuration: number,\n startTime: number,\n commitTime: number,\n ) => boolean;\n}\n\n// Kept on globalThis so the ESM and CommonJS builds, or two bundled copies,\n// share one runtime instead of a component silently reading an empty one.\nconst KEY = Symbol.for(\"react-render-pulse.runtime\");\n\ntype RuntimeHolder = { [KEY]?: PulseRuntime | null };\n\nexport function getRuntime(): PulseRuntime | null {\n return (globalThis as RuntimeHolder)[KEY] ?? null;\n}\n\nexport function setRuntime(runtime: PulseRuntime | null): void {\n (globalThis as RuntimeHolder)[KEY] = runtime;\n}\n","import { createAggregator } from \"../aggregate/aggregator.js\";\nimport { getDeviceContext } from \"../context/device.js\";\nimport { createRouteReader, defaultGetRoute } from \"../context/route.js\";\nimport { createConsoleExporter } from \"../export/console.js\";\nimport { createModeDetector } from \"../modes/detect.js\";\nimport { observeLongTasks } from \"../observe/longtasks.js\";\nimport { createRenderWindows } from \"../observe/renderWindows.js\";\nimport { createSessionId, shouldSample } from \"../sample/sample.js\";\nimport { serializePayload } from \"../transport/payload.js\";\nimport { createTransport } from \"../transport/transport.js\";\nimport type { Exporter, LongTaskEvent, Mode, ModeOption, Payload, RenderEvent, RenderPhase } from \"../types.js\";\nimport { guard, setDebug } from \"./guard.js\";\nimport { getRuntime, setRuntime, type PulseRuntime } from \"./runtime.js\";\n\nexport interface RenderPulseOptions {\n /** Where batches are POSTed. Without one, batches only go to `exporters` (and the console, see `debug`). */\n endpoint?: string | undefined;\n /** Fraction of sessions that collect anything, 0–1, decided once at startup. Defaults to 1. */\n sampleRate?: number | undefined;\n /** `\"profiling\"`, `\"lightweight\"`, or `\"auto\"` (the default): lightweight until React's profiling build is detected. */\n mode?: ModeOption | undefined;\n /** Renders slower than this are counted as slow. Defaults to 16 (one frame at 60 Hz). */\n slowRenderMs?: number | undefined;\n /** Long tasks and long animation frames shorter than this are ignored. Defaults to 50. */\n longTaskMs?: number | undefined;\n /** Your app's version, e.g. from `import.meta.env.VITE_APP_VERSION`, for comparing releases. */\n appVersion?: string | undefined;\n /** Returns the current route, ideally a template like `/orders/:id`. Defaults to the path with numeric segments replaced by `:id`. */\n getRoute?: (() => string) | undefined;\n /** Also sends a batch this often while the page is visible. `0` sends only when the page is hidden. Defaults to 180000 (3 minutes). */\n flushIntervalMs?: number | undefined;\n /** Largest batch in bytes; bigger ones are trimmed. Defaults to 60000, under the 64 KB `sendBeacon` limit. Capped at 65536, the collector's default limit. */\n maxPayloadBytes?: number | undefined;\n /** Most area/route pairs per batch. Defaults to 200, capped at 1000 (the collector's limit). */\n maxAreas?: number | undefined;\n /** Slowest renders kept per area per batch. Defaults to 5, capped at 50 (the collector's limit). */\n worstCases?: number | undefined;\n /** Receive measurements and batches, e.g. `createOtelExporter`. */\n exporters?: Exporter[] | undefined;\n /** Logs each batch to the console and reports internal errors as warnings. Defaults to true when there's no `endpoint`. */\n debug?: boolean | undefined;\n}\n\nexport interface RenderPulse {\n /** False when this session was left out by `sampleRate`; nothing is measured or sent. */\n readonly sampled: boolean;\n /** The current measurement mode. In `\"auto\"`, this changes to `\"profiling\"` once the profiling build is detected. */\n readonly mode: Mode;\n /** Builds a batch from everything measured since the last one and sends it. Returns it, or `null` when there was nothing to send. */\n flush: () => Payload | null;\n /** Stops observing and sending, and detaches `<RenderMonitor>`s, which keep rendering their children as usual. */\n destroy: () => void;\n}\n\nconst INERT: RenderPulse = { sampled: false, mode: \"lightweight\", flush: () => null, destroy: () => {} };\n\nlet active: { destroy: () => void } | null = null;\n\n/**\n * Starts render-pulse. Call it once, before your app renders. Calling it\n * again replaces the previous setup. On the server it does nothing.\n */\nexport function initRenderPulse(options: RenderPulseOptions = {}): RenderPulse {\n if (typeof window === \"undefined\") return INERT;\n return guard(() => start(options), INERT);\n}\n\nfunction start(options: RenderPulseOptions): RenderPulse {\n active?.destroy();\n\n const {\n endpoint,\n sampleRate = 1,\n mode: modeOption = \"auto\",\n slowRenderMs = 16,\n longTaskMs = 50,\n flushIntervalMs = 180_000,\n debug = !endpoint,\n } = options;\n // Clamped to what the collector accepts, so a generous setting can't get every batch rejected.\n const maxPayloadBytes = clamp(options.maxPayloadBytes ?? 60_000, 1_000, 65_536);\n const maxAreas = clamp(options.maxAreas ?? 200, 1, 1000);\n const worstCases = clamp(options.worstCases ?? 5, 0, 50);\n const appVersion = options.appVersion?.slice(0, 100);\n\n setDebug(debug);\n const sampled = shouldSample(sampleRate);\n const now = () => performance.now();\n const sessionStart = now();\n const startedAt = Date.now();\n const sessionId = createSessionId();\n let seq = 0;\n\n const detector = createModeDetector(modeOption);\n const readRoute = createRouteReader(options.getRoute ?? defaultGetRoute);\n const route = () => guard(readRoute, \"/\").slice(0, 500);\n const areaId = (id: string) => (id ? id.slice(0, 200) : \"(unnamed)\");\n const exporters = [...(options.exporters ?? []), ...(debug ? [createConsoleExporter()] : [])];\n const aggregator = createAggregator({ slowRenderMs, maxAreas, worstCases, sessionStart });\n const windows = createRenderWindows();\n const transport = endpoint ? createTransport({ endpoint, maxAttempts: 3, maxQueued: 5 }) : null;\n\n function record(event: RenderEvent): void {\n aggregator.addRender(event);\n windows.add(event.area, event.startTime, event.endTime, event.duration);\n for (const exporter of exporters) guard(() => exporter.onRender?.(event), undefined);\n }\n\n function recordLongTask(event: LongTaskEvent): void {\n aggregator.addLongTask(event);\n for (const exporter of exporters) guard(() => exporter.onLongTask?.(event), undefined);\n }\n\n const runtime: PulseRuntime = {\n sampled,\n now,\n measuresWithMarks: () => sampled && detector.measuresWithMarks(),\n recordMarks(area, phase, startTime, endTime) {\n if (!sampled) return;\n guard(() => {\n const duration = endTime - startTime;\n // Marks measurements are lightweight-mode data even in a profiling session (e.g. from useRenderMonitor).\n record({ area: areaId(area), route: route(), phase, duration, startTime, endTime, mode: \"lightweight\" });\n }, undefined);\n },\n recordProfiler(area, phase: RenderPhase, actualDuration, startTime, commitTime) {\n if (!sampled) return false;\n return guard(() => {\n detector.profilerFired();\n if (detector.current() !== \"profiling\") return false;\n record({\n area: areaId(area),\n route: route(),\n phase,\n duration: actualDuration,\n startTime,\n endTime: commitTime,\n mode: \"profiling\",\n });\n return true;\n }, false);\n },\n };\n\n function flush(unloading: boolean): Payload | null {\n if (!sampled || aggregator.isEmpty()) return null;\n\n return guard(() => {\n const batch = aggregator.drain();\n const payload: Payload = {\n v: 1,\n session: {\n id: sessionId,\n seq: ++seq,\n mode: detector.current(),\n route: route(),\n appVersion: appVersion ?? null,\n startedAt,\n sentAt: Date.now(),\n },\n device: getDeviceContext(),\n summary: batch.summary,\n areas: batch.areas,\n longTasks: batch.longTasks,\n longFrames: batch.longFrames,\n dropped: { areas: batch.droppedAreas },\n };\n\n for (const exporter of exporters) guard(() => exporter.onFlush?.(payload), undefined);\n\n if (transport) {\n const body = serializePayload(payload, maxPayloadBytes);\n if (body) transport.send(body, { unloading });\n }\n\n return payload;\n }, null);\n }\n\n const cleanups: (() => void)[] = [];\n\n if (sampled) {\n cleanups.push(\n observeLongTasks(longTaskMs, (task) =>\n guard(() => {\n const area = windows.findArea(task.startTime, task.startTime + task.duration);\n recordLongTask({ ...task, area, route: route() });\n }, undefined),\n ),\n );\n\n // `pagehide` covers browsers (older Safari) that skip `visibilitychange` on unload.\n const onHidden = () => {\n if (document.visibilityState === \"hidden\") flush(true);\n };\n const onPageHide = () => flush(true);\n document.addEventListener(\"visibilitychange\", onHidden);\n window.addEventListener(\"pagehide\", onPageHide);\n cleanups.push(() => {\n document.removeEventListener(\"visibilitychange\", onHidden);\n window.removeEventListener(\"pagehide\", onPageHide);\n });\n\n if (flushIntervalMs > 0) {\n const timer = setInterval(() => flush(false), flushIntervalMs);\n cleanups.push(() => clearInterval(timer));\n }\n }\n\n setRuntime(runtime);\n\n const instance = {\n destroy() {\n for (const cleanup of cleanups.splice(0)) guard(cleanup, undefined);\n if (getRuntime() === runtime) setRuntime(null);\n if (active === instance) active = null;\n },\n };\n active = instance;\n\n return {\n sampled,\n get mode() {\n return detector.current();\n },\n flush: () => flush(false),\n destroy: instance.destroy,\n };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Number.isFinite(value) ? Math.min(max, Math.max(min, Math.floor(value))) : min;\n}\n","\"use client\";\n\nimport { Profiler, useCallback, useRef, type ProfilerOnRenderCallback, type ReactNode } from \"react\";\nimport { guard } from \"../runtime/guard.js\";\nimport { getRuntime } from \"../runtime/runtime.js\";\nimport { useIsomorphicLayoutEffect } from \"../runtime/useIsomorphicLayoutEffect.js\";\n\nexport interface RenderMonitorProps {\n /** Names this area in reports, e.g. `\"orders-table\"`. Areas with the same id are reported together. */\n id: string;\n children?: ReactNode;\n}\n\ninterface MonitorState {\n id: string;\n mounted: boolean;\n start: number;\n end: number;\n /** The `start` of the last recorded measurement, so StrictMode's repeated effects don't record it twice. */\n recordedStart: number;\n /** The latest marks measurement, waiting to see whether a `<Profiler>` callback for the same commit supersedes it. */\n pending: PendingMeasurement | null;\n}\n\ninterface PendingMeasurement {\n start: number;\n end: number;\n phase: \"mount\" | \"update\";\n superseded: boolean;\n}\n\n/**\n * Measures the renders of its children and reports them under `id`.\n *\n * The tree it renders is the same in every mode and whether or not the\n * session is sampled (a `<Profiler>` around two empty marker components), so\n * switching modes or calling `initRenderPulse` late never remounts children.\n * - Profiling mode: React's `<Profiler>` reports each commit's actual render time.\n * - Lightweight mode: the time between the start and end markers rendering,\n * which brackets the children's render. Updates that start below this area\n * (a child's own state change) don't re-render the markers, so they aren't\n * seen; profiling mode catches those.\n */\nexport function RenderMonitor({ id, children }: RenderMonitorProps): ReactNode {\n const stateRef = useRef<MonitorState | null>(null);\n if (stateRef.current === null) {\n stateRef.current = { id, mounted: false, start: 0, end: 0, recordedStart: -1, pending: null };\n }\n const state = stateRef.current;\n state.id = id;\n\n const onRender = useCallback<ProfilerOnRenderCallback>(\n (_id, phase, actualDuration, _baseDuration, startTime, commitTime) => {\n // When the Profiler's measurement of this commit is used, a marks measurement of it is a duplicate.\n if (getRuntime()?.recordProfiler(state.id, phase, actualDuration, startTime, commitTime) && state.pending) {\n state.pending.superseded = true;\n }\n },\n [state],\n );\n\n return (\n <Profiler id={id} onRender={onRender}>\n <StartMarker state={state} />\n {children}\n <EndMarker state={state} />\n </Profiler>\n );\n}\n\nfunction StartMarker({ state }: { state: MonitorState }): null {\n const runtime = getRuntime();\n if (runtime?.measuresWithMarks()) state.start = runtime.now();\n return null;\n}\n\nfunction EndMarker({ state }: { state: MonitorState }): null {\n const runtime = getRuntime();\n if (runtime?.measuresWithMarks()) state.end = runtime.now();\n\n // Layout effects only run for renders that commit, so abandoned concurrent renders aren't recorded.\n useIsomorphicLayoutEffect(() => {\n guard(() => {\n const current = getRuntime();\n if (!current?.measuresWithMarks() || state.start === state.recordedStart || state.end < state.start) return;\n\n state.recordedStart = state.start;\n const measurement: PendingMeasurement = {\n start: state.start,\n end: state.end,\n phase: state.mounted ? \"update\" : \"mount\",\n superseded: false,\n };\n state.pending = measurement;\n state.mounted = true;\n\n // The Profiler's callback for this commit (if the profiling build is in\n // use) runs later in the same commit, so wait a microtask before\n // deciding this measurement is the only one. Each commit gets its own\n // object, so several commits in one task are all kept.\n queueMicrotask(() => {\n if (state.pending === measurement) state.pending = null;\n if (!measurement.superseded) {\n getRuntime()?.recordMarks(state.id, measurement.phase, measurement.start, measurement.end);\n }\n });\n }, undefined);\n });\n\n return null;\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\n// useLayoutEffect warns during SSR on React 18; measurements only happen in the browser.\nexport const useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n","\"use client\";\n\nimport { useRef } from \"react\";\nimport { guard } from \"../runtime/guard.js\";\nimport { getRuntime } from \"../runtime/runtime.js\";\nimport { useIsomorphicLayoutEffect } from \"../runtime/useIsomorphicLayoutEffect.js\";\n\n/**\n * Tracks the renders of the component that calls it, reported under `id`.\n *\n * Render counts are exact. The duration runs from the hook's call to the\n * commit, so it also includes anything React renders after this component\n * in the same update: an upper bound. Wrap the component in `<RenderMonitor>`\n * when you need its own render time.\n */\nexport function useRenderMonitor(id: string): void {\n const state = useRef({ start: 0, recordedStart: -1, mounted: false }).current;\n const runtime = getRuntime();\n if (runtime?.sampled) state.start = runtime.now();\n\n useIsomorphicLayoutEffect(() => {\n guard(() => {\n const current = getRuntime();\n // `recordedStart` stops StrictMode's repeated effects from recording one render twice.\n if (!current?.sampled || state.start === state.recordedStart) return;\n\n state.recordedStart = state.start;\n current.recordMarks(id, state.mounted ? \"update\" : \"mount\", state.start, current.now());\n state.mounted = true;\n }, undefined);\n });\n}\n"],"mappings":"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","names":["sum","start","window","debug","useRef","useRef"]}
@@ -0,0 +1,16 @@
1
+ import type { Mode, ModeOption } from "../types.cjs";
2
+ export interface ModeDetector {
3
+ /** The mode measurements are currently taken in. */
4
+ current: () => Mode;
5
+ /** True when the performance-mark measurements in `<RenderMonitor>` should run. */
6
+ measuresWithMarks: () => boolean;
7
+ /** Called on every `<Profiler>` callback; in `"auto"` mode the first one switches to profiling mode. */
8
+ profilerFired: () => void;
9
+ }
10
+ /**
11
+ * React only calls `<Profiler onRender>` in development and in its profiling
12
+ * build, so a callback arriving is proof that per-subtree timings are
13
+ * available. There's no public API to ask which build is loaded, and this
14
+ * avoids relying on React internals.
15
+ */
16
+ export declare function createModeDetector(option: ModeOption): ModeDetector;
@@ -0,0 +1,17 @@
1
+ import type { Mode, ModeOption } from "../types.js";
2
+ export interface ModeDetector {
3
+ /** The mode measurements are currently taken in. */
4
+ current: () => Mode;
5
+ /** True when the performance-mark measurements in `<RenderMonitor>` should run. */
6
+ measuresWithMarks: () => boolean;
7
+ /** Called on every `<Profiler>` callback; in `"auto"` mode the first one switches to profiling mode. */
8
+ profilerFired: () => void;
9
+ }
10
+ /**
11
+ * React only calls `<Profiler onRender>` in development and in its profiling
12
+ * build, so a callback arriving is proof that per-subtree timings are
13
+ * available. There's no public API to ask which build is loaded, and this
14
+ * avoids relying on React internals.
15
+ */
16
+ export declare function createModeDetector(option: ModeOption): ModeDetector;
17
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/modes/detect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,mFAAmF;IACnF,iBAAiB,EAAE,MAAM,OAAO,CAAC;IACjC,wGAAwG;IACxG,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAUnE"}
@@ -0,0 +1,12 @@
1
+ import type { LongTaskKind } from "../types.cjs";
2
+ export interface ObservedLongTask {
3
+ kind: LongTaskKind;
4
+ startTime: number;
5
+ duration: number;
6
+ }
7
+ /**
8
+ * Observes long tasks (Chromium) and long animation frames (Chromium 123+)
9
+ * at or above `thresholdMs`. Entry types the browser doesn't support are
10
+ * skipped. Returns a function that stops observing.
11
+ */
12
+ export declare function observeLongTasks(thresholdMs: number, onEntry: (task: ObservedLongTask) => void): () => void;
@@ -0,0 +1,13 @@
1
+ import type { LongTaskKind } from "../types.js";
2
+ export interface ObservedLongTask {
3
+ kind: LongTaskKind;
4
+ startTime: number;
5
+ duration: number;
6
+ }
7
+ /**
8
+ * Observes long tasks (Chromium) and long animation frames (Chromium 123+)
9
+ * at or above `thresholdMs`. Entry types the browser doesn't support are
10
+ * skipped. Returns a function that stops observing.
11
+ */
12
+ export declare function observeLongTasks(thresholdMs: number, onEntry: (task: ObservedLongTask) => void): () => void;
13
+ //# sourceMappingURL=longtasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"longtasks.d.ts","sourceRoot":"","sources":["../../src/observe/longtasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI,CA0B3G"}
@@ -0,0 +1,17 @@
1
+ export interface RenderWindows {
2
+ /** Records that `area` rendered for `duration` ms somewhere between `start` and `end`. */
3
+ add: (area: string, start: number, end: number, duration: number) => void;
4
+ /** The area to blame for a main-thread block over `[start, end]`, or `null` when no recent render overlaps it. */
5
+ findArea: (start: number, end: number) => string | null;
6
+ }
7
+ /**
8
+ * A fixed-size ring of recent render windows. Long-task entries arrive a
9
+ * little after the fact, so attribution looks back over the last `size` renders.
10
+ *
11
+ * Nested areas overlap the same block, and an outer area's duration includes
12
+ * its inner ones. So the blame goes to the innermost overlapping area that
13
+ * still accounts for at least half of the longest overlapping render: an
14
+ * inner area that's responsible for most of the time, rather than always the
15
+ * outermost wrapper or a tiny sibling that happened to render in the same commit.
16
+ */
17
+ export declare function createRenderWindows(size?: number): RenderWindows;
@@ -0,0 +1,18 @@
1
+ export interface RenderWindows {
2
+ /** Records that `area` rendered for `duration` ms somewhere between `start` and `end`. */
3
+ add: (area: string, start: number, end: number, duration: number) => void;
4
+ /** The area to blame for a main-thread block over `[start, end]`, or `null` when no recent render overlaps it. */
5
+ findArea: (start: number, end: number) => string | null;
6
+ }
7
+ /**
8
+ * A fixed-size ring of recent render windows. Long-task entries arrive a
9
+ * little after the fact, so attribution looks back over the last `size` renders.
10
+ *
11
+ * Nested areas overlap the same block, and an outer area's duration includes
12
+ * its inner ones. So the blame goes to the innermost overlapping area that
13
+ * still accounts for at least half of the longest overlapping render: an
14
+ * inner area that's responsible for most of the time, rather than always the
15
+ * outermost wrapper or a tiny sibling that happened to render in the same commit.
16
+ */
17
+ export declare function createRenderWindows(size?: number): RenderWindows;
18
+ //# sourceMappingURL=renderWindows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWindows.d.ts","sourceRoot":"","sources":["../../src/observe/renderWindows.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,aAAa;IAC5B,0FAA0F;IAC1F,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kHAAkH;IAClH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,SAAM,GAAG,aAAa,CAwB7D"}
package/dist/otel.cjs ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/otel.ts
21
+ var otel_exports = {};
22
+ __export(otel_exports, {
23
+ createOtelExporter: () => createOtelExporter
24
+ });
25
+ module.exports = __toCommonJS(otel_exports);
26
+
27
+ // src/export/otel.ts
28
+ function createOtelExporter(options) {
29
+ const { meter, slowRenderMs = 16 } = options;
30
+ const renderDuration = meter.createHistogram("render_pulse.render.duration", {
31
+ description: "Render duration of a monitored React area",
32
+ unit: "ms"
33
+ });
34
+ const slowRenders = meter.createCounter("render_pulse.render.slow", {
35
+ description: "Renders slower than the slow-render threshold"
36
+ });
37
+ const longTaskDuration = meter.createHistogram("render_pulse.longtask.duration", {
38
+ description: "Long main-thread tasks and long animation frames",
39
+ unit: "ms"
40
+ });
41
+ return {
42
+ onRender(event) {
43
+ const attributes = {
44
+ "render_pulse.area": event.area,
45
+ "render_pulse.route": event.route,
46
+ "render_pulse.phase": event.phase,
47
+ "render_pulse.mode": event.mode
48
+ };
49
+ renderDuration.record(event.duration, attributes);
50
+ if (event.duration > slowRenderMs) slowRenders.add(1, attributes);
51
+ },
52
+ onLongTask(event) {
53
+ longTaskDuration.record(event.duration, {
54
+ "render_pulse.area": event.area ?? "(none)",
55
+ "render_pulse.route": event.route,
56
+ "render_pulse.kind": event.kind
57
+ });
58
+ }
59
+ };
60
+ }
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ createOtelExporter
64
+ });
65
+ //# sourceMappingURL=otel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/otel.ts","../src/export/otel.ts"],"sourcesContent":["// OpenTelemetry export, kept out of the main entry so apps that don't use it don't ship it.\nexport { createOtelExporter } from \"./export/otel.js\";\nexport type { OtelExporterOptions, OtelMeterLike } from \"./export/otel.js\";\n","import type { Exporter } from \"../types.js\";\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/**\n * The parts of an OpenTelemetry `Meter` this exporter uses. Structurally\n * compatible with `@opentelemetry/api`'s `Meter`, so there's no dependency on it.\n */\nexport interface OtelMeterLike {\n createHistogram: (\n name: string,\n options?: { description?: string; unit?: string },\n ) => { record: (value: number, attributes?: Attributes) => void };\n createCounter: (\n name: string,\n options?: { description?: string; unit?: string },\n ) => { add: (value: number, attributes?: Attributes) => void };\n}\n\nexport interface OtelExporterOptions {\n /** e.g. `metrics.getMeter(\"my-app\")` from `@opentelemetry/api`. */\n meter: OtelMeterLike;\n /** Renders slower than this also increment `render_pulse.render.slow`. Defaults to 16. */\n slowRenderMs?: number | undefined;\n}\n\n/**\n * Records every measurement as OpenTelemetry metrics:\n * - `render_pulse.render.duration` (histogram, ms) with `render_pulse.area`, `render_pulse.route`, `render_pulse.phase`, `render_pulse.mode`\n * - `render_pulse.render.slow` (counter) with the same attributes\n * - `render_pulse.longtask.duration` (histogram, ms) with `render_pulse.area`, `render_pulse.route`, `render_pulse.kind`\n */\nexport function createOtelExporter(options: OtelExporterOptions): Exporter {\n const { meter, slowRenderMs = 16 } = options;\n\n const renderDuration = meter.createHistogram(\"render_pulse.render.duration\", {\n description: \"Render duration of a monitored React area\",\n unit: \"ms\",\n });\n const slowRenders = meter.createCounter(\"render_pulse.render.slow\", {\n description: \"Renders slower than the slow-render threshold\",\n });\n const longTaskDuration = meter.createHistogram(\"render_pulse.longtask.duration\", {\n description: \"Long main-thread tasks and long animation frames\",\n unit: \"ms\",\n });\n\n return {\n onRender(event) {\n const attributes: Attributes = {\n \"render_pulse.area\": event.area,\n \"render_pulse.route\": event.route,\n \"render_pulse.phase\": event.phase,\n \"render_pulse.mode\": event.mode,\n };\n renderDuration.record(event.duration, attributes);\n if (event.duration > slowRenderMs) slowRenders.add(1, attributes);\n },\n onLongTask(event) {\n longTaskDuration.record(event.duration, {\n \"render_pulse.area\": event.area ?? \"(none)\",\n \"render_pulse.route\": event.route,\n \"render_pulse.kind\": event.kind,\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgCO,SAAS,mBAAmB,SAAwC;AACzE,QAAM,EAAE,OAAO,eAAe,GAAG,IAAI;AAErC,QAAM,iBAAiB,MAAM,gBAAgB,gCAAgC;AAAA,IAC3E,aAAa;AAAA,IACb,MAAM;AAAA,EACR,CAAC;AACD,QAAM,cAAc,MAAM,cAAc,4BAA4B;AAAA,IAClE,aAAa;AAAA,EACf,CAAC;AACD,QAAM,mBAAmB,MAAM,gBAAgB,kCAAkC;AAAA,IAC/E,aAAa;AAAA,IACb,MAAM;AAAA,EACR,CAAC;AAED,SAAO;AAAA,IACL,SAAS,OAAO;AACd,YAAM,aAAyB;AAAA,QAC7B,qBAAqB,MAAM;AAAA,QAC3B,sBAAsB,MAAM;AAAA,QAC5B,sBAAsB,MAAM;AAAA,QAC5B,qBAAqB,MAAM;AAAA,MAC7B;AACA,qBAAe,OAAO,MAAM,UAAU,UAAU;AAChD,UAAI,MAAM,WAAW,aAAc,aAAY,IAAI,GAAG,UAAU;AAAA,IAClE;AAAA,IACA,WAAW,OAAO;AAChB,uBAAiB,OAAO,MAAM,UAAU;AAAA,QACtC,qBAAqB,MAAM,QAAQ;AAAA,QACnC,sBAAsB,MAAM;AAAA,QAC5B,qBAAqB,MAAM;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,2 @@
1
+ export { createOtelExporter } from "./export/otel.cjs";
2
+ export type { OtelExporterOptions, OtelMeterLike } from "./export/otel.cjs";
package/dist/otel.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { createOtelExporter } from "./export/otel.js";
2
+ export type { OtelExporterOptions, OtelMeterLike } from "./export/otel.js";
3
+ //# sourceMappingURL=otel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/otel.js ADDED
@@ -0,0 +1,38 @@
1
+ // src/export/otel.ts
2
+ function createOtelExporter(options) {
3
+ const { meter, slowRenderMs = 16 } = options;
4
+ const renderDuration = meter.createHistogram("render_pulse.render.duration", {
5
+ description: "Render duration of a monitored React area",
6
+ unit: "ms"
7
+ });
8
+ const slowRenders = meter.createCounter("render_pulse.render.slow", {
9
+ description: "Renders slower than the slow-render threshold"
10
+ });
11
+ const longTaskDuration = meter.createHistogram("render_pulse.longtask.duration", {
12
+ description: "Long main-thread tasks and long animation frames",
13
+ unit: "ms"
14
+ });
15
+ return {
16
+ onRender(event) {
17
+ const attributes = {
18
+ "render_pulse.area": event.area,
19
+ "render_pulse.route": event.route,
20
+ "render_pulse.phase": event.phase,
21
+ "render_pulse.mode": event.mode
22
+ };
23
+ renderDuration.record(event.duration, attributes);
24
+ if (event.duration > slowRenderMs) slowRenders.add(1, attributes);
25
+ },
26
+ onLongTask(event) {
27
+ longTaskDuration.record(event.duration, {
28
+ "render_pulse.area": event.area ?? "(none)",
29
+ "render_pulse.route": event.route,
30
+ "render_pulse.kind": event.kind
31
+ });
32
+ }
33
+ };
34
+ }
35
+ export {
36
+ createOtelExporter
37
+ };
38
+ //# sourceMappingURL=otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/export/otel.ts"],"sourcesContent":["import type { Exporter } from \"../types.js\";\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/**\n * The parts of an OpenTelemetry `Meter` this exporter uses. Structurally\n * compatible with `@opentelemetry/api`'s `Meter`, so there's no dependency on it.\n */\nexport interface OtelMeterLike {\n createHistogram: (\n name: string,\n options?: { description?: string; unit?: string },\n ) => { record: (value: number, attributes?: Attributes) => void };\n createCounter: (\n name: string,\n options?: { description?: string; unit?: string },\n ) => { add: (value: number, attributes?: Attributes) => void };\n}\n\nexport interface OtelExporterOptions {\n /** e.g. `metrics.getMeter(\"my-app\")` from `@opentelemetry/api`. */\n meter: OtelMeterLike;\n /** Renders slower than this also increment `render_pulse.render.slow`. Defaults to 16. */\n slowRenderMs?: number | undefined;\n}\n\n/**\n * Records every measurement as OpenTelemetry metrics:\n * - `render_pulse.render.duration` (histogram, ms) with `render_pulse.area`, `render_pulse.route`, `render_pulse.phase`, `render_pulse.mode`\n * - `render_pulse.render.slow` (counter) with the same attributes\n * - `render_pulse.longtask.duration` (histogram, ms) with `render_pulse.area`, `render_pulse.route`, `render_pulse.kind`\n */\nexport function createOtelExporter(options: OtelExporterOptions): Exporter {\n const { meter, slowRenderMs = 16 } = options;\n\n const renderDuration = meter.createHistogram(\"render_pulse.render.duration\", {\n description: \"Render duration of a monitored React area\",\n unit: \"ms\",\n });\n const slowRenders = meter.createCounter(\"render_pulse.render.slow\", {\n description: \"Renders slower than the slow-render threshold\",\n });\n const longTaskDuration = meter.createHistogram(\"render_pulse.longtask.duration\", {\n description: \"Long main-thread tasks and long animation frames\",\n unit: \"ms\",\n });\n\n return {\n onRender(event) {\n const attributes: Attributes = {\n \"render_pulse.area\": event.area,\n \"render_pulse.route\": event.route,\n \"render_pulse.phase\": event.phase,\n \"render_pulse.mode\": event.mode,\n };\n renderDuration.record(event.duration, attributes);\n if (event.duration > slowRenderMs) slowRenders.add(1, attributes);\n },\n onLongTask(event) {\n longTaskDuration.record(event.duration, {\n \"render_pulse.area\": event.area ?? \"(none)\",\n \"render_pulse.route\": event.route,\n \"render_pulse.kind\": event.kind,\n });\n },\n };\n}\n"],"mappings":";AAgCO,SAAS,mBAAmB,SAAwC;AACzE,QAAM,EAAE,OAAO,eAAe,GAAG,IAAI;AAErC,QAAM,iBAAiB,MAAM,gBAAgB,gCAAgC;AAAA,IAC3E,aAAa;AAAA,IACb,MAAM;AAAA,EACR,CAAC;AACD,QAAM,cAAc,MAAM,cAAc,4BAA4B;AAAA,IAClE,aAAa;AAAA,EACf,CAAC;AACD,QAAM,mBAAmB,MAAM,gBAAgB,kCAAkC;AAAA,IAC/E,aAAa;AAAA,IACb,MAAM;AAAA,EACR,CAAC;AAED,SAAO;AAAA,IACL,SAAS,OAAO;AACd,YAAM,aAAyB;AAAA,QAC7B,qBAAqB,MAAM;AAAA,QAC3B,sBAAsB,MAAM;AAAA,QAC5B,sBAAsB,MAAM;AAAA,QAC5B,qBAAqB,MAAM;AAAA,MAC7B;AACA,qBAAe,OAAO,MAAM,UAAU,UAAU;AAChD,UAAI,MAAM,WAAW,aAAc,aAAY,IAAI,GAAG,UAAU;AAAA,IAClE;AAAA,IACA,WAAW,OAAO;AAChB,uBAAiB,OAAO,MAAM,UAAU;AAAA,QACtC,qBAAqB,MAAM,QAAQ;AAAA,QACnC,sBAAsB,MAAM;AAAA,QAC5B,qBAAqB,MAAM;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,7 @@
1
+ export declare function setDebug(value: boolean): void;
2
+ /**
3
+ * Runs `fn`, swallowing anything it throws, so an error inside render-pulse
4
+ * (or a user-supplied exporter or `getRoute`) never reaches the app. With
5
+ * `debug`, the error is logged instead of silently dropped.
6
+ */
7
+ export declare function guard<T>(fn: () => T, fallback: T): T;
@@ -0,0 +1,8 @@
1
+ export declare function setDebug(value: boolean): void;
2
+ /**
3
+ * Runs `fn`, swallowing anything it throws, so an error inside render-pulse
4
+ * (or a user-supplied exporter or `getRoute`) never reaches the app. With
5
+ * `debug`, the error is logged instead of silently dropped.
6
+ */
7
+ export declare function guard<T>(fn: () => T, fallback: T): T;
8
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/runtime/guard.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAOpD"}
@@ -0,0 +1,44 @@
1
+ import type { Exporter, Mode, ModeOption, Payload } from "../types.cjs";
2
+ export interface RenderPulseOptions {
3
+ /** Where batches are POSTed. Without one, batches only go to `exporters` (and the console, see `debug`). */
4
+ endpoint?: string | undefined;
5
+ /** Fraction of sessions that collect anything, 0–1, decided once at startup. Defaults to 1. */
6
+ sampleRate?: number | undefined;
7
+ /** `"profiling"`, `"lightweight"`, or `"auto"` (the default): lightweight until React's profiling build is detected. */
8
+ mode?: ModeOption | undefined;
9
+ /** Renders slower than this are counted as slow. Defaults to 16 (one frame at 60 Hz). */
10
+ slowRenderMs?: number | undefined;
11
+ /** Long tasks and long animation frames shorter than this are ignored. Defaults to 50. */
12
+ longTaskMs?: number | undefined;
13
+ /** Your app's version, e.g. from `import.meta.env.VITE_APP_VERSION`, for comparing releases. */
14
+ appVersion?: string | undefined;
15
+ /** Returns the current route, ideally a template like `/orders/:id`. Defaults to the path with numeric segments replaced by `:id`. */
16
+ getRoute?: (() => string) | undefined;
17
+ /** Also sends a batch this often while the page is visible. `0` sends only when the page is hidden. Defaults to 180000 (3 minutes). */
18
+ flushIntervalMs?: number | undefined;
19
+ /** Largest batch in bytes; bigger ones are trimmed. Defaults to 60000, under the 64 KB `sendBeacon` limit. Capped at 65536, the collector's default limit. */
20
+ maxPayloadBytes?: number | undefined;
21
+ /** Most area/route pairs per batch. Defaults to 200, capped at 1000 (the collector's limit). */
22
+ maxAreas?: number | undefined;
23
+ /** Slowest renders kept per area per batch. Defaults to 5, capped at 50 (the collector's limit). */
24
+ worstCases?: number | undefined;
25
+ /** Receive measurements and batches, e.g. `createOtelExporter`. */
26
+ exporters?: Exporter[] | undefined;
27
+ /** Logs each batch to the console and reports internal errors as warnings. Defaults to true when there's no `endpoint`. */
28
+ debug?: boolean | undefined;
29
+ }
30
+ export interface RenderPulse {
31
+ /** False when this session was left out by `sampleRate`; nothing is measured or sent. */
32
+ readonly sampled: boolean;
33
+ /** The current measurement mode. In `"auto"`, this changes to `"profiling"` once the profiling build is detected. */
34
+ readonly mode: Mode;
35
+ /** Builds a batch from everything measured since the last one and sends it. Returns it, or `null` when there was nothing to send. */
36
+ flush: () => Payload | null;
37
+ /** Stops observing and sending, and detaches `<RenderMonitor>`s, which keep rendering their children as usual. */
38
+ destroy: () => void;
39
+ }
40
+ /**
41
+ * Starts render-pulse. Call it once, before your app renders. Calling it
42
+ * again replaces the previous setup. On the server it does nothing.
43
+ */
44
+ export declare function initRenderPulse(options?: RenderPulseOptions): RenderPulse;
@@ -0,0 +1,45 @@
1
+ import type { Exporter, Mode, ModeOption, Payload } from "../types.js";
2
+ export interface RenderPulseOptions {
3
+ /** Where batches are POSTed. Without one, batches only go to `exporters` (and the console, see `debug`). */
4
+ endpoint?: string | undefined;
5
+ /** Fraction of sessions that collect anything, 0–1, decided once at startup. Defaults to 1. */
6
+ sampleRate?: number | undefined;
7
+ /** `"profiling"`, `"lightweight"`, or `"auto"` (the default): lightweight until React's profiling build is detected. */
8
+ mode?: ModeOption | undefined;
9
+ /** Renders slower than this are counted as slow. Defaults to 16 (one frame at 60 Hz). */
10
+ slowRenderMs?: number | undefined;
11
+ /** Long tasks and long animation frames shorter than this are ignored. Defaults to 50. */
12
+ longTaskMs?: number | undefined;
13
+ /** Your app's version, e.g. from `import.meta.env.VITE_APP_VERSION`, for comparing releases. */
14
+ appVersion?: string | undefined;
15
+ /** Returns the current route, ideally a template like `/orders/:id`. Defaults to the path with numeric segments replaced by `:id`. */
16
+ getRoute?: (() => string) | undefined;
17
+ /** Also sends a batch this often while the page is visible. `0` sends only when the page is hidden. Defaults to 180000 (3 minutes). */
18
+ flushIntervalMs?: number | undefined;
19
+ /** Largest batch in bytes; bigger ones are trimmed. Defaults to 60000, under the 64 KB `sendBeacon` limit. Capped at 65536, the collector's default limit. */
20
+ maxPayloadBytes?: number | undefined;
21
+ /** Most area/route pairs per batch. Defaults to 200, capped at 1000 (the collector's limit). */
22
+ maxAreas?: number | undefined;
23
+ /** Slowest renders kept per area per batch. Defaults to 5, capped at 50 (the collector's limit). */
24
+ worstCases?: number | undefined;
25
+ /** Receive measurements and batches, e.g. `createOtelExporter`. */
26
+ exporters?: Exporter[] | undefined;
27
+ /** Logs each batch to the console and reports internal errors as warnings. Defaults to true when there's no `endpoint`. */
28
+ debug?: boolean | undefined;
29
+ }
30
+ export interface RenderPulse {
31
+ /** False when this session was left out by `sampleRate`; nothing is measured or sent. */
32
+ readonly sampled: boolean;
33
+ /** The current measurement mode. In `"auto"`, this changes to `"profiling"` once the profiling build is detected. */
34
+ readonly mode: Mode;
35
+ /** Builds a batch from everything measured since the last one and sends it. Returns it, or `null` when there was nothing to send. */
36
+ flush: () => Payload | null;
37
+ /** Stops observing and sending, and detaches `<RenderMonitor>`s, which keep rendering their children as usual. */
38
+ destroy: () => void;
39
+ }
40
+ /**
41
+ * Starts render-pulse. Call it once, before your app renders. Calling it
42
+ * again replaces the previous setup. On the server it does nothing.
43
+ */
44
+ export declare function initRenderPulse(options?: RenderPulseOptions): RenderPulse;
45
+ //# sourceMappingURL=initRenderPulse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initRenderPulse.d.ts","sourceRoot":"","sources":["../../src/runtime/initRenderPulse.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAiB,IAAI,EAAE,UAAU,EAAE,OAAO,EAA4B,MAAM,aAAa,CAAC;AAIhH,MAAM,WAAW,kBAAkB;IACjC,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,wHAAwH;IACxH,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9B,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,sIAAsI;IACtI,QAAQ,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;IACtC,uIAAuI;IACvI,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,8JAA8J;IAC9J,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,mEAAmE;IACnE,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC;IACnC,2HAA2H;IAC3H,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,qHAAqH;IACrH,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,qIAAqI;IACrI,KAAK,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IAC5B,kHAAkH;IAClH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAG7E"}
@@ -0,0 +1,13 @@
1
+ import type { RenderPhase } from "../types.cjs";
2
+ /** What `<RenderMonitor>` and `useRenderMonitor` call into. Every method is safe to call and never throws. */
3
+ export interface PulseRuntime {
4
+ readonly sampled: boolean;
5
+ /** True when `<RenderMonitor>` should take performance-mark measurements (lightweight mode). */
6
+ measuresWithMarks: () => boolean;
7
+ now: () => number;
8
+ recordMarks: (area: string, phase: RenderPhase, start: number, end: number) => void;
9
+ /** Returns true when the measurement was recorded, i.e. the session is in profiling mode. */
10
+ recordProfiler: (area: string, phase: RenderPhase, actualDuration: number, startTime: number, commitTime: number) => boolean;
11
+ }
12
+ export declare function getRuntime(): PulseRuntime | null;
13
+ export declare function setRuntime(runtime: PulseRuntime | null): void;
@@ -0,0 +1,14 @@
1
+ import type { RenderPhase } from "../types.js";
2
+ /** What `<RenderMonitor>` and `useRenderMonitor` call into. Every method is safe to call and never throws. */
3
+ export interface PulseRuntime {
4
+ readonly sampled: boolean;
5
+ /** True when `<RenderMonitor>` should take performance-mark measurements (lightweight mode). */
6
+ measuresWithMarks: () => boolean;
7
+ now: () => number;
8
+ recordMarks: (area: string, phase: RenderPhase, start: number, end: number) => void;
9
+ /** Returns true when the measurement was recorded, i.e. the session is in profiling mode. */
10
+ recordProfiler: (area: string, phase: RenderPhase, actualDuration: number, startTime: number, commitTime: number) => boolean;
11
+ }
12
+ export declare function getRuntime(): PulseRuntime | null;
13
+ export declare function setRuntime(runtime: PulseRuntime | null): void;
14
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,8GAA8G;AAC9G,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gGAAgG;IAChG,iBAAiB,EAAE,MAAM,OAAO,CAAC;IACjC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF,6FAA6F;IAC7F,cAAc,EAAE,CACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,EAClB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC;CACd;AAQD,wBAAgB,UAAU,IAAI,YAAY,GAAG,IAAI,CAEhD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAE7D"}
@@ -0,0 +1,2 @@
1
+ import { useLayoutEffect } from "react";
2
+ export declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
@@ -0,0 +1,3 @@
1
+ import { useLayoutEffect } from "react";
2
+ export declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
3
+ //# sourceMappingURL=useIsomorphicLayoutEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsomorphicLayoutEffect.d.ts","sourceRoot":"","sources":["../../src/runtime/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,eAAe,EAAE,MAAM,OAAO,CAAC;AAGnD,eAAO,MAAM,yBAAyB,wBAA8D,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Decides once per session whether to collect, so unsampled sessions pay
3
+ * almost nothing. `sampleRate` is clamped to 0–1.
4
+ */
5
+ export declare function shouldSample(sampleRate: number, random?: () => number): boolean;
6
+ /** A random session id. Not derived from anything about the user, and never persisted. */
7
+ export declare function createSessionId(): string;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Decides once per session whether to collect, so unsampled sessions pay
3
+ * almost nothing. `sampleRate` is clamped to 0–1.
4
+ */
5
+ export declare function shouldSample(sampleRate: number, random?: () => number): boolean;
6
+ /** A random session id. Not derived from anything about the user, and never persisted. */
7
+ export declare function createSessionId(): string;
8
+ //# sourceMappingURL=sample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sample.d.ts","sourceRoot":"","sources":["../../src/sample/sample.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,MAAoB,GAAG,OAAO,CAI5F;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,IAAI,MAAM,CASxC"}
@@ -0,0 +1,8 @@
1
+ import type { Payload } from "../types.cjs";
2
+ export declare function byteLength(text: string): number;
3
+ /**
4
+ * Serializes a payload, shrinking it until it fits in `maxBytes`: first the
5
+ * worst-case lists go, then the areas with the fewest renders. Returns `null`
6
+ * when even an empty payload doesn't fit.
7
+ */
8
+ export declare function serializePayload(payload: Payload, maxBytes: number): string | null;
@@ -0,0 +1,9 @@
1
+ import type { Payload } from "../types.js";
2
+ export declare function byteLength(text: string): number;
3
+ /**
4
+ * Serializes a payload, shrinking it until it fits in `maxBytes`: first the
5
+ * worst-case lists go, then the areas with the fewest renders. Returns `null`
6
+ * when even an empty payload doesn't fit.
7
+ */
8
+ export declare function serializePayload(payload: Payload, maxBytes: number): string | null;
9
+ //# sourceMappingURL=payload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/transport/payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsBlF"}
@@ -0,0 +1,18 @@
1
+ export interface TransportOptions {
2
+ endpoint: string;
3
+ /** Failed sends are retried on later flushes, up to this many attempts in total. */
4
+ maxAttempts: number;
5
+ /** Most failed bodies kept for retry; the oldest are dropped past this. */
6
+ maxQueued: number;
7
+ }
8
+ export interface Transport {
9
+ /**
10
+ * Sends a serialized payload. With `unloading`, uses `navigator.sendBeacon`
11
+ * (falling back to `fetch` with `keepalive`), since the page may be gone
12
+ * before a normal request finishes. Never throws or rejects.
13
+ */
14
+ send: (body: string, options: {
15
+ unloading: boolean;
16
+ }) => void;
17
+ }
18
+ export declare function createTransport(options: TransportOptions): Transport;