@flotrace/runtime-core 2.2.3 → 2.3.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.
package/dist/index.mjs CHANGED
@@ -1,3 +1,20 @@
1
+ import {
2
+ FLOTRACE_SOURCE,
3
+ JSX_RUNTIME_ACTIVE_KEY,
4
+ clearCallSiteRenders,
5
+ computeCallSiteId,
6
+ computeCallSiteMetricsPayload,
7
+ detectInlineLiterals,
8
+ getCallSiteRenderRate,
9
+ getCallSiteRenders,
10
+ isJsxRuntimeActive,
11
+ markJsxRuntimeActive,
12
+ normalizeJsxSourcePath,
13
+ readJsxSourceFromFiber,
14
+ recordCallSiteRender,
15
+ setDuplicateKeyEmitter
16
+ } from "./chunk-QLOJU5F2.mjs";
17
+
1
18
  // src/types.ts
2
19
  var DEFAULT_CONFIG = {
3
20
  port: 3457,
@@ -129,11 +146,7 @@ function serializeValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet())
129
146
  for (let i = 0; i < Math.min(keys.length, MAX_OBJECT_KEYS); i++) {
130
147
  const key = keys[i];
131
148
  try {
132
- result[key] = serializeValue(
133
- value[key],
134
- depth + 1,
135
- seen
136
- );
149
+ result[key] = serializeValue(value[key], depth + 1, seen);
137
150
  } catch {
138
151
  result[key] = { __type: "truncated", originalType: "error" };
139
152
  }
@@ -234,7 +247,12 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
234
247
  frameworkName: this.config.frameworkName,
235
248
  frameworkVersion: this.config.frameworkVersion,
236
249
  reactNativeVersion: this.config.reactNativeVersion,
237
- runtimeVersion: this.config.runtimeVersion
250
+ runtimeVersion: this.config.runtimeVersion,
251
+ // P5: JSX runtime adoption signal — read at WS-open time so
252
+ // multiple fibers have already rendered by the moment we report.
253
+ // `isJsxRuntimeActive` reads `globalThis[Symbol.for('flotrace.jsx-runtime-active')]`,
254
+ // which the dev jsx-runtime sets on first jsxDEV call.
255
+ jsxRuntimeActive: isJsxRuntimeActive()
238
256
  });
239
257
  this.flush();
240
258
  };
@@ -560,26 +578,31 @@ function classifyFromDebugLabel(state, index, effects, effectIdx, debugLabel) {
560
578
  const ms = state.memoizedState;
561
579
  const normalizedLabel = debugLabel.toLowerCase().replace(/\s/g, "");
562
580
  const labelMap = {
563
- "usestate": "useState",
564
- "usereducer": "useReducer",
565
- "useref": "useRef",
566
- "usememo": "useMemo",
567
- "usecallback": "useCallback",
568
- "useeffect": "useEffect",
569
- "uselayouteffect": "useLayoutEffect",
570
- "useinsertioneffect": "useInsertionEffect",
571
- "usecontext": "useContext",
572
- "useimperativehandle": "useImperativeHandle",
573
- "usedebugvalue": "useDebugValue",
574
- "usetransition": "useTransition",
575
- "usedeferredvalue": "useDeferredValue",
576
- "useid": "useId",
577
- "usesyncexternalstore": "useSyncExternalStore",
578
- "useoptimistic": "useOptimistic",
579
- "useformstatus": "useFormStatus"
581
+ usestate: "useState",
582
+ usereducer: "useReducer",
583
+ useref: "useRef",
584
+ usememo: "useMemo",
585
+ usecallback: "useCallback",
586
+ useeffect: "useEffect",
587
+ uselayouteffect: "useLayoutEffect",
588
+ useinsertioneffect: "useInsertionEffect",
589
+ usecontext: "useContext",
590
+ useimperativehandle: "useImperativeHandle",
591
+ usedebugvalue: "useDebugValue",
592
+ usetransition: "useTransition",
593
+ usedeferredvalue: "useDeferredValue",
594
+ useid: "useId",
595
+ usesyncexternalstore: "useSyncExternalStore",
596
+ useoptimistic: "useOptimistic",
597
+ useformstatus: "useFormStatus"
580
598
  };
581
599
  const hookType = labelMap[normalizedLabel] ?? "unknown";
582
- const base = { index, type: hookType, value: serializeValue(ms, 0, /* @__PURE__ */ new WeakSet()), debugLabel };
600
+ const base = {
601
+ index,
602
+ type: hookType,
603
+ value: serializeValue(ms, 0, /* @__PURE__ */ new WeakSet()),
604
+ debugLabel
605
+ };
583
606
  if (hookType === "useEffect" || hookType === "useLayoutEffect" || hookType === "useInsertionEffect") {
584
607
  if (effectIdx < effects.length) {
585
608
  const effect = effects[effectIdx];
@@ -1046,7 +1069,13 @@ var ClassComponent = 1;
1046
1069
  var ForwardRef = 11;
1047
1070
  var MemoComponent = 14;
1048
1071
  var SimpleMemoComponent = 15;
1049
- var USER_TAGS = /* @__PURE__ */ new Set([FunctionComponent, ClassComponent, ForwardRef, MemoComponent, SimpleMemoComponent]);
1072
+ var USER_TAGS = /* @__PURE__ */ new Set([
1073
+ FunctionComponent,
1074
+ ClassComponent,
1075
+ ForwardRef,
1076
+ MemoComponent,
1077
+ SimpleMemoComponent
1078
+ ]);
1050
1079
  function isMemoizedFiber(fiber) {
1051
1080
  return fiber.tag === MemoComponent || fiber.tag === SimpleMemoComponent;
1052
1081
  }
@@ -1130,13 +1159,15 @@ function buildCascadeTree(rootFiber, triggers) {
1130
1159
  triggerByName.set(t.componentName, t);
1131
1160
  }
1132
1161
  }
1133
- const stack = [{
1134
- fiber: rootFiber,
1135
- depth: 0,
1136
- parentRerendered: false,
1137
- parentNode: null,
1138
- isRoot: true
1139
- }];
1162
+ const stack = [
1163
+ {
1164
+ fiber: rootFiber,
1165
+ depth: 0,
1166
+ parentRerendered: false,
1167
+ parentNode: null,
1168
+ isRoot: true
1169
+ }
1170
+ ];
1140
1171
  while (stack.length > 0) {
1141
1172
  const entry = stack.pop();
1142
1173
  const { fiber, depth, parentRerendered, parentNode, isRoot } = entry;
@@ -1147,7 +1178,13 @@ function buildCascadeTree(rootFiber, triggers) {
1147
1178
  if (isNewMount && !didRender) {
1148
1179
  let child2 = fiber.child;
1149
1180
  while (child2) {
1150
- stack.push({ fiber: child2, depth: depth + 1, parentRerendered: false, parentNode, isRoot: false });
1181
+ stack.push({
1182
+ fiber: child2,
1183
+ depth: depth + 1,
1184
+ parentRerendered: false,
1185
+ parentNode,
1186
+ isRoot: false
1187
+ });
1151
1188
  child2 = child2.sibling;
1152
1189
  }
1153
1190
  continue;
@@ -1155,7 +1192,13 @@ function buildCascadeTree(rootFiber, triggers) {
1155
1192
  if (!USER_TAGS.has(fiber.tag)) {
1156
1193
  let child2 = fiber.child;
1157
1194
  while (child2) {
1158
- stack.push({ fiber: child2, depth: depth + 1, parentRerendered: didRender || parentRerendered, parentNode, isRoot: false });
1195
+ stack.push({
1196
+ fiber: child2,
1197
+ depth: depth + 1,
1198
+ parentRerendered: didRender || parentRerendered,
1199
+ parentNode,
1200
+ isRoot: false
1201
+ });
1159
1202
  child2 = child2.sibling;
1160
1203
  }
1161
1204
  continue;
@@ -1164,7 +1207,13 @@ function buildCascadeTree(rootFiber, triggers) {
1164
1207
  if (reason === null) {
1165
1208
  let child2 = fiber.child;
1166
1209
  while (child2) {
1167
- stack.push({ fiber: child2, depth: depth + 1, parentRerendered: false, parentNode, isRoot: false });
1210
+ stack.push({
1211
+ fiber: child2,
1212
+ depth: depth + 1,
1213
+ parentRerendered: false,
1214
+ parentNode,
1215
+ isRoot: false
1216
+ });
1168
1217
  child2 = child2.sibling;
1169
1218
  }
1170
1219
  continue;
@@ -1190,7 +1239,11 @@ function buildCascadeTree(rootFiber, triggers) {
1190
1239
  triggerId,
1191
1240
  children: [],
1192
1241
  depth,
1193
- isMemoized: isMemoizedFiber(fiber)
1242
+ isMemoized: isMemoizedFiber(fiber),
1243
+ // P6: JSX-runtime attribution — read from fiber.memoizedProps directly.
1244
+ // Same source the walker uses, so cascade nodes align with LiveTreeNode
1245
+ // attribution for the same user component.
1246
+ jsxSource: readJsxSourceFromFiber(fiber)
1194
1247
  };
1195
1248
  totalComponents++;
1196
1249
  if (reason === "parent-cascade") {
@@ -1223,7 +1276,10 @@ function analyzeCascade(root, triggers) {
1223
1276
  try {
1224
1277
  const finishedLanes = getFinishedLanes(root);
1225
1278
  const lane = classifyLanes(finishedLanes);
1226
- const { rootCauses, totalComponents, avoidableCount, avoidableDuration } = buildCascadeTree(root.current, triggers);
1279
+ const { rootCauses, totalComponents, avoidableCount, avoidableDuration } = buildCascadeTree(
1280
+ root.current,
1281
+ triggers
1282
+ );
1227
1283
  if (totalComponents === 0) return null;
1228
1284
  const totalDuration = rootCauses.reduce((sum, n) => sum + n.subtreeDuration, 0);
1229
1285
  const triggerIds = triggers.map((t) => t.triggerId);
@@ -1305,7 +1361,8 @@ function shouldFlagRename(value) {
1305
1361
  if (value === null || value === void 0) return false;
1306
1362
  if (typeof value !== "object") return false;
1307
1363
  if (Array.isArray(value) && value.length === 0) return false;
1308
- if (!Array.isArray(value) && Object.keys(value).length === 0) return false;
1364
+ if (!Array.isArray(value) && Object.keys(value).length === 0)
1365
+ return false;
1309
1366
  return true;
1310
1367
  }
1311
1368
  function computePropIntersectionRatio(nodeProps, childrenProps) {
@@ -1514,13 +1571,24 @@ function runAnalysis(tree, fiberRefMap2) {
1514
1571
  propKey: p.propKey,
1515
1572
  role,
1516
1573
  hookCount: hookCounts.get(p.nodeId) ?? 0,
1517
- hasContextHook: contextFlags.get(p.nodeId) ?? false
1574
+ hasContextHook: contextFlags.get(p.nodeId) ?? false,
1575
+ // P6: propagate JSX-runtime attribution from the tree node so the
1576
+ // drill-chain detail can render `(file:line)` per step + click-
1577
+ // to-IDE on each component along the chain. Undefined when the
1578
+ // user hasn't opted into the JSX runtime.
1579
+ jsxSource: n?.jsxSource
1518
1580
  };
1519
1581
  });
1520
1582
  const passthroughCount = chainNodes.filter((n) => n.role === "passthrough").length;
1521
1583
  const sourceNode = nodeMap.get(sourceId);
1522
1584
  const renames = path.flatMap(
1523
- (p, idx) => p.isRename ? [{ atNodeId: p.nodeId, fromKey: idx > 0 ? path[idx - 1].propKey : sourcePropName, toKey: p.propKey }] : []
1585
+ (p, idx) => p.isRename ? [
1586
+ {
1587
+ atNodeId: p.nodeId,
1588
+ fromKey: idx > 0 ? path[idx - 1].propKey : sourcePropName,
1589
+ toKey: p.propKey
1590
+ }
1591
+ ] : []
1524
1592
  );
1525
1593
  chains.push({
1526
1594
  chainId: makeChainId(sourceId, fp, consumerNodeId),
@@ -1615,10 +1683,7 @@ var SERVER_COMPONENT_PATTERNS = [
1615
1683
  /[\\/]app[\\/].+[\\/]error\.[jt]sx?$/
1616
1684
  // Next.js error UI
1617
1685
  ];
1618
- var SERVER_REFERENCE_PATTERNS = [
1619
- /_ServerReference$/,
1620
- /^RSC_/
1621
- ];
1686
+ var SERVER_REFERENCE_PATTERNS = [/_ServerReference$/, /^RSC_/];
1622
1687
  var detectionEmitted = false;
1623
1688
  function maybeEmitNextjsContext(client2) {
1624
1689
  if (detectionEmitted) return;
@@ -1712,7 +1777,9 @@ function scanActionStateChanges(fiberRefMap2, client2) {
1712
1777
  for (const [nodeId, fiber] of fiberRefMap2) {
1713
1778
  const entries = extractActionEntries(fiber);
1714
1779
  if (!entries) continue;
1715
- const snapshot = JSON.stringify(entries.map((e) => ({ i: e.hookIndex, p: e.isPending, s: e.state })));
1780
+ const snapshot = JSON.stringify(
1781
+ entries.map((e) => ({ i: e.hookIndex, p: e.isPending, s: e.state }))
1782
+ );
1716
1783
  if (prevActionStateMap.get(nodeId) === snapshot) continue;
1717
1784
  prevActionStateMap.set(nodeId, snapshot);
1718
1785
  const componentName = nodeId.split("/").pop()?.replace(/-\d+$/, "") ?? "Unknown";
@@ -1822,7 +1889,8 @@ function tagFetchData(obj, requestId, depth = 0) {
1822
1889
  const limit = Math.min(obj.length, FETCH_ORIGIN_TAG_ARRAY_LIMIT);
1823
1890
  for (let i = 0; i < limit; i++) tagFetchData(obj[i], requestId, depth + 1);
1824
1891
  } else {
1825
- for (const val of Object.values(obj)) tagFetchData(val, requestId, depth + 1);
1892
+ for (const val of Object.values(obj))
1893
+ tagFetchData(val, requestId, depth + 1);
1826
1894
  }
1827
1895
  }
1828
1896
  function hasActiveTags() {
@@ -1858,6 +1926,344 @@ function scanForOrigin(obj, depth, ignoreTTL) {
1858
1926
  return void 0;
1859
1927
  }
1860
1928
 
1929
+ // src/fiberDebugLogger.ts
1930
+ var MAX_FIBER_RECORDS = 500;
1931
+ var MAX_TREE_RECORDS = 50;
1932
+ var MAX_TOP_NAMES_PER_SNAPSHOT = 10;
1933
+ var MAX_CONTEXTS_PER_FIBER = 8;
1934
+ var totalFiberEvents = 0;
1935
+ var recordingStartedAt = null;
1936
+ var fiberRecords = /* @__PURE__ */ new Map();
1937
+ var treeRecords = [];
1938
+ function isRecording() {
1939
+ return Boolean(globalThis.__FT_DEBUG);
1940
+ }
1941
+ function setFiberDebug(enabled) {
1942
+ globalThis.__FT_DEBUG = enabled;
1943
+ if (enabled) {
1944
+ console.info(
1945
+ "%c[FT debug]%c recording started \u2014 call %c__ft.dump()%c to view, %c__ft.clear()%c to reset, %c__ft.download()%c to export",
1946
+ "background:#1e293b;color:#7dd3fc;padding:1px 6px;border-radius:3px;font-weight:600;",
1947
+ "color:#94a3b8;",
1948
+ "color:#a78bfa;font-weight:600;",
1949
+ "color:#94a3b8;",
1950
+ "color:#a78bfa;font-weight:600;",
1951
+ "color:#94a3b8;",
1952
+ "color:#a78bfa;font-weight:600;",
1953
+ "color:#94a3b8;"
1954
+ );
1955
+ }
1956
+ }
1957
+ function isFiberFunctionType(t) {
1958
+ return typeof t === "function";
1959
+ }
1960
+ function isFiberObjectType(t) {
1961
+ return typeof t === "object" && t !== null;
1962
+ }
1963
+ function isClassComponentType(t) {
1964
+ if (typeof t !== "function") return false;
1965
+ const proto = t.prototype;
1966
+ return typeof proto?.isReactComponent !== "undefined";
1967
+ }
1968
+ function describeFiberType(fiber) {
1969
+ const type = fiber.type;
1970
+ if (typeof type === "string") {
1971
+ return {
1972
+ kind: "host",
1973
+ name: type,
1974
+ displayName: void 0,
1975
+ resolved: type,
1976
+ looksMinified: false
1977
+ };
1978
+ }
1979
+ if (isFiberFunctionType(type)) {
1980
+ const isClass = isClassComponentType(type);
1981
+ const resolved = type.displayName ?? type.name ?? "Anonymous";
1982
+ return {
1983
+ kind: isClass ? "class" : "function",
1984
+ name: type.name,
1985
+ displayName: type.displayName,
1986
+ resolved,
1987
+ looksMinified: looksMinified(resolved)
1988
+ };
1989
+ }
1990
+ if (isFiberObjectType(type)) {
1991
+ if (type.render) {
1992
+ const resolved2 = type.render.displayName ?? type.render.name ?? "ForwardRef";
1993
+ return {
1994
+ kind: "forwardRef",
1995
+ name: type.render.name,
1996
+ displayName: type.render.displayName,
1997
+ resolved: resolved2,
1998
+ looksMinified: looksMinified(resolved2)
1999
+ };
2000
+ }
2001
+ if (type.type) {
2002
+ const resolved2 = type.type.displayName ?? type.type.name ?? "Memo";
2003
+ return {
2004
+ kind: "memo",
2005
+ name: type.type.name,
2006
+ displayName: type.type.displayName,
2007
+ resolved: resolved2,
2008
+ looksMinified: looksMinified(resolved2)
2009
+ };
2010
+ }
2011
+ const resolved = type.displayName ?? type.name ?? "Unknown";
2012
+ return {
2013
+ kind: "unknown",
2014
+ name: type.name,
2015
+ displayName: type.displayName,
2016
+ resolved,
2017
+ looksMinified: looksMinified(resolved)
2018
+ };
2019
+ }
2020
+ return {
2021
+ kind: "unknown",
2022
+ name: void 0,
2023
+ displayName: void 0,
2024
+ resolved: "Unknown",
2025
+ looksMinified: false
2026
+ };
2027
+ }
2028
+ function looksMinified(name) {
2029
+ return /^[a-z_$][a-z0-9_$]?$/i.test(name);
2030
+ }
2031
+ function logFiberType(fiber, context) {
2032
+ if (!isRecording()) return;
2033
+ const info = describeFiberType(fiber);
2034
+ const key = info.resolved;
2035
+ const now2 = Date.now();
2036
+ if (recordingStartedAt === null) recordingStartedAt = now2;
2037
+ totalFiberEvents += 1;
2038
+ const existing = fiberRecords.get(key);
2039
+ if (existing) {
2040
+ existing.count += 1;
2041
+ existing.lastSeen = now2;
2042
+ if (context && existing.contexts.size < MAX_CONTEXTS_PER_FIBER) {
2043
+ existing.contexts.add(context);
2044
+ }
2045
+ if (existing.exampleKey === void 0 && typeof fiber.key === "string") {
2046
+ existing.exampleKey = fiber.key;
2047
+ }
2048
+ return;
2049
+ }
2050
+ if (fiberRecords.size >= MAX_FIBER_RECORDS) {
2051
+ evictOldestFiber();
2052
+ }
2053
+ fiberRecords.set(key, {
2054
+ name: info.resolved,
2055
+ rawName: info.name,
2056
+ rawDisplayName: info.displayName,
2057
+ kind: info.kind,
2058
+ fiberTag: fiber.tag,
2059
+ looksMinified: info.looksMinified,
2060
+ count: 1,
2061
+ contexts: new Set(context ? [context] : []),
2062
+ firstSeen: now2,
2063
+ lastSeen: now2,
2064
+ source: fiber._debugSource ?? void 0,
2065
+ exampleKey: typeof fiber.key === "string" ? fiber.key : void 0
2066
+ });
2067
+ }
2068
+ function evictOldestFiber() {
2069
+ let oldestKey;
2070
+ let oldestTime = Infinity;
2071
+ for (const [key, rec] of fiberRecords) {
2072
+ if (rec.lastSeen < oldestTime) {
2073
+ oldestTime = rec.lastSeen;
2074
+ oldestKey = key;
2075
+ }
2076
+ }
2077
+ if (oldestKey) fiberRecords.delete(oldestKey);
2078
+ }
2079
+ function walkStats(node, depth, acc) {
2080
+ acc.total += 1;
2081
+ if (depth > acc.maxDepth) acc.maxDepth = depth;
2082
+ if (looksMinified(node.name)) acc.minifiedLike += 1;
2083
+ acc.byName.set(node.name, (acc.byName.get(node.name) ?? 0) + 1);
2084
+ for (const child of node.children) walkStats(child, depth + 1, acc);
2085
+ }
2086
+ function logTreeSnapshot(tree, context) {
2087
+ if (!isRecording() || !tree) return;
2088
+ const stats = { total: 0, minifiedLike: 0, byName: /* @__PURE__ */ new Map(), maxDepth: 0 };
2089
+ walkStats(tree, 0, stats);
2090
+ const topNames = [...stats.byName.entries()].sort((a, b) => b[1] - a[1]).slice(0, MAX_TOP_NAMES_PER_SNAPSHOT).map(([n, c]) => `${n}:${c}`).join(",");
2091
+ treeRecords.push({
2092
+ ts: Date.now(),
2093
+ ctx: context ?? "",
2094
+ rootName: tree.name,
2095
+ totalNodes: stats.total,
2096
+ maxDepth: stats.maxDepth,
2097
+ minifiedLike: stats.minifiedLike,
2098
+ topNames
2099
+ });
2100
+ if (treeRecords.length > MAX_TREE_RECORDS) treeRecords.shift();
2101
+ }
2102
+ var logTreeSummary = logTreeSnapshot;
2103
+ function installConsoleApi() {
2104
+ if (globalThis.__ft) return;
2105
+ const api = {
2106
+ dump() {
2107
+ const fiberRows = serializeFiberRecords();
2108
+ const snapRows = serializeTreeRecords();
2109
+ const minifiedCount = fiberRows.filter((r) => r.looksMinified).length;
2110
+ const elapsedSec = recordingStartedAt === null ? 0 : (Date.now() - recordingStartedAt) / 1e3;
2111
+ const summary = [
2112
+ {
2113
+ metric: "uniqueComponents",
2114
+ value: fiberRecords.size
2115
+ },
2116
+ {
2117
+ metric: "totalFiberEvents",
2118
+ value: totalFiberEvents
2119
+ },
2120
+ {
2121
+ metric: "minifiedLike",
2122
+ value: `${minifiedCount} / ${fiberRecords.size}`
2123
+ },
2124
+ {
2125
+ metric: "snapshots",
2126
+ value: treeRecords.length
2127
+ },
2128
+ {
2129
+ metric: "recordingSec",
2130
+ value: elapsedSec.toFixed(1)
2131
+ }
2132
+ ];
2133
+ console.groupCollapsed(
2134
+ `%c[FT debug] dump%c \u2014 ${fiberRecords.size} components, ${totalFiberEvents} events, ${treeRecords.length} snapshots`,
2135
+ "background:#1e293b;color:#7dd3fc;padding:1px 6px;border-radius:3px;font-weight:600;",
2136
+ "color:#94a3b8;"
2137
+ );
2138
+ console.log("Summary:");
2139
+ console.table(summary, ["metric", "value"]);
2140
+ console.log("Fibers \u2014 every observed component (sorted by call count):");
2141
+ console.table(fiberRows, [
2142
+ "name",
2143
+ "rawName",
2144
+ "rawDisplayName",
2145
+ "kind",
2146
+ "fiberTag",
2147
+ "count",
2148
+ "looksMinified",
2149
+ "exampleKey",
2150
+ "contexts",
2151
+ "file",
2152
+ "line",
2153
+ "firstSeenAt",
2154
+ "lastSeenAt",
2155
+ "lastAgoSec"
2156
+ ]);
2157
+ console.log("Tree snapshots \u2014 newest last:");
2158
+ console.table(snapRows, [
2159
+ "ts",
2160
+ "ctx",
2161
+ "rootName",
2162
+ "totalNodes",
2163
+ "maxDepth",
2164
+ "minifiedLike",
2165
+ "topNames"
2166
+ ]);
2167
+ console.groupEnd();
2168
+ },
2169
+ fibers() {
2170
+ console.table(serializeFiberRecords(), [
2171
+ "name",
2172
+ "rawName",
2173
+ "rawDisplayName",
2174
+ "kind",
2175
+ "fiberTag",
2176
+ "count",
2177
+ "looksMinified",
2178
+ "exampleKey",
2179
+ "contexts",
2180
+ "file",
2181
+ "line",
2182
+ "firstSeenAt",
2183
+ "lastSeenAt",
2184
+ "lastAgoSec"
2185
+ ]);
2186
+ },
2187
+ snapshots() {
2188
+ console.table(serializeTreeRecords());
2189
+ },
2190
+ tail(n = 20) {
2191
+ console.table(treeRecords.slice(-n));
2192
+ },
2193
+ clear() {
2194
+ fiberRecords.clear();
2195
+ treeRecords.length = 0;
2196
+ console.info("[FT debug] cleared");
2197
+ },
2198
+ size() {
2199
+ return { fibers: fiberRecords.size, snapshots: treeRecords.length };
2200
+ },
2201
+ export() {
2202
+ return { fibers: serializeFiberRecords(), snapshots: treeRecords.slice() };
2203
+ },
2204
+ download(filename) {
2205
+ const data = api.export();
2206
+ const json = JSON.stringify(data, null, 2);
2207
+ const docRef = globalThis.document;
2208
+ const URLRef = globalThis.URL;
2209
+ if (!docRef || !URLRef || typeof URLRef.createObjectURL !== "function") {
2210
+ console.warn(
2211
+ "[FT debug] download() requires a browser environment \u2014 printing JSON instead"
2212
+ );
2213
+ console.log(json);
2214
+ return;
2215
+ }
2216
+ const blob = new Blob([json], { type: "application/json" });
2217
+ const url = URLRef.createObjectURL(blob);
2218
+ const a = docRef.createElement("a");
2219
+ a.href = url;
2220
+ a.download = filename ?? `flotrace-debug-${Date.now()}.json`;
2221
+ a.click();
2222
+ URLRef.revokeObjectURL(url);
2223
+ }
2224
+ };
2225
+ globalThis.__ft = api;
2226
+ }
2227
+ installConsoleApi();
2228
+ function formatTime(ms) {
2229
+ const d = new Date(ms);
2230
+ const hh = String(d.getHours()).padStart(2, "0");
2231
+ const mm = String(d.getMinutes()).padStart(2, "0");
2232
+ const ss = String(d.getSeconds()).padStart(2, "0");
2233
+ const mss = String(d.getMilliseconds()).padStart(3, "0");
2234
+ return `${hh}:${mm}:${ss}.${mss}`;
2235
+ }
2236
+ function serializeFiberRecords() {
2237
+ const now2 = Date.now();
2238
+ return [...fiberRecords.values()].sort((a, b) => b.count - a.count).map((r) => ({
2239
+ name: r.name,
2240
+ rawName: r.rawName ?? "",
2241
+ rawDisplayName: r.rawDisplayName ?? "",
2242
+ kind: r.kind,
2243
+ fiberTag: r.fiberTag ?? "",
2244
+ count: r.count,
2245
+ looksMinified: r.looksMinified,
2246
+ exampleKey: r.exampleKey ?? "",
2247
+ contexts: [...r.contexts].join(","),
2248
+ file: r.source?.fileName ?? "",
2249
+ line: r.source?.lineNumber ?? "",
2250
+ firstSeenAt: formatTime(r.firstSeen),
2251
+ lastSeenAt: formatTime(r.lastSeen),
2252
+ lastAgoSec: ((now2 - r.lastSeen) / 1e3).toFixed(1)
2253
+ }));
2254
+ }
2255
+ function serializeTreeRecords() {
2256
+ return treeRecords.map((t) => ({
2257
+ ts: formatTime(t.ts),
2258
+ ctx: t.ctx,
2259
+ rootName: t.rootName,
2260
+ totalNodes: t.totalNodes,
2261
+ maxDepth: t.maxDepth,
2262
+ minifiedLike: t.minifiedLike,
2263
+ topNames: t.topNames
2264
+ }));
2265
+ }
2266
+
1861
2267
  // src/fiberTreeWalker.ts
1862
2268
  var FIBER_TAGS = {
1863
2269
  FunctionComponent: 0,
@@ -2000,6 +2406,7 @@ function debugLog(...args) {
2000
2406
  }
2001
2407
  var fiberRefMap = /* @__PURE__ */ new Map();
2002
2408
  function getComponentName2(fiber) {
2409
+ logFiberType(fiber, "getName");
2003
2410
  const type = fiber.type;
2004
2411
  if (!type) return "Unknown";
2005
2412
  if (typeof type === "function") {
@@ -2125,6 +2532,8 @@ var FRAMEWORK_PATH_PATTERNS = [
2125
2532
  /formik/
2126
2533
  ];
2127
2534
  function resolveEffectiveSourcePath(fiber) {
2535
+ const jsxSrc = readJsxSourceFromFiber(fiber);
2536
+ if (jsxSrc) return jsxSrc.fileName;
2128
2537
  if (fiber._debugSource?.fileName) return fiber._debugSource.fileName;
2129
2538
  const ownerHit = walkAncestors(
2130
2539
  fiber._debugOwner ?? null,
@@ -2140,6 +2549,14 @@ function resolveEffectiveSourcePath(fiber) {
2140
2549
  }
2141
2550
  return null;
2142
2551
  }
2552
+ function resolveSourceConfidence(fiber, isFramework, isLibrary, precomputedJsxSource) {
2553
+ if (isFramework || isLibrary) return "package";
2554
+ const jsxSrc = precomputedJsxSource ?? readJsxSourceFromFiber(fiber);
2555
+ if (jsxSrc) return "exact";
2556
+ if (fiber._debugSource?.fileName) return "exact";
2557
+ if (resolveEffectiveSourcePath(fiber)) return "inferred";
2558
+ return "unknown";
2559
+ }
2143
2560
  var STOP_WALK = /* @__PURE__ */ Symbol("stop-walk");
2144
2561
  function walkAncestors(start, maxHops, next, visit) {
2145
2562
  let cur = start;
@@ -2306,13 +2723,7 @@ function walkFiber(fiber, parentId, sharedNameCountMap, depth = 0, inSuspenseFal
2306
2723
  { reason: renderReason },
2307
2724
  current.actualDuration
2308
2725
  );
2309
- const children = walkFiber(
2310
- current.child,
2311
- nodeId,
2312
- void 0,
2313
- depth + 1,
2314
- inSuspenseFallback
2315
- );
2726
+ const children = walkFiber(current.child, nodeId, void 0, depth + 1, inSuspenseFallback);
2316
2727
  const truncatedChildren = children.length > MAX_CHILDREN_PER_NODE ? children.slice(0, MAX_CHILDREN_PER_NODE) : children;
2317
2728
  const framework = isFrameworkComponent(current, name) || void 0;
2318
2729
  const queryHashes = detectQueryObserverHashes(current);
@@ -2320,6 +2731,13 @@ function walkFiber(fiber, parentId, sharedNameCountMap, depth = 0, inSuspenseFal
2320
2731
  const compilerStatus = detectCompilerStatus(current);
2321
2732
  const isServerComponent = detectServerComponent(current) || void 0;
2322
2733
  const libraryName = framework ? void 0 : detectLibraryName(current, name);
2734
+ const jsxSource = readJsxSourceFromFiber(current);
2735
+ const sourceConfidence = resolveSourceConfidence(
2736
+ current,
2737
+ framework === true,
2738
+ libraryName !== void 0,
2739
+ jsxSource
2740
+ );
2323
2741
  const node = {
2324
2742
  id: nodeId,
2325
2743
  name,
@@ -2328,8 +2746,8 @@ function walkFiber(fiber, parentId, sharedNameCountMap, depth = 0, inSuspenseFal
2328
2746
  renderPhase,
2329
2747
  renderReason,
2330
2748
  renderDuration: current.actualDuration,
2331
- filePath: current._debugSource?.fileName,
2332
- lineNumber: current._debugSource?.lineNumber,
2749
+ filePath: jsxSource?.fileName ?? current._debugSource?.fileName,
2750
+ lineNumber: jsxSource?.lineNumber ?? current._debugSource?.lineNumber,
2333
2751
  isFramework: framework,
2334
2752
  reactKey: resolveEffectiveReactKey(current),
2335
2753
  queryHashes,
@@ -2340,7 +2758,9 @@ function walkFiber(fiber, parentId, sharedNameCountMap, depth = 0, inSuspenseFal
2340
2758
  compilerStatus,
2341
2759
  isServerComponent,
2342
2760
  isLibrary: libraryName !== void 0 ? true : void 0,
2343
- libraryName
2761
+ libraryName,
2762
+ jsxSource,
2763
+ sourceConfidence
2344
2764
  };
2345
2765
  if (!walkerOptions.pruneSubtree?.(node)) {
2346
2766
  nodes.push(node);
@@ -2414,11 +2834,7 @@ function buildTreeFromFiberRoot(root) {
2414
2834
  }
2415
2835
  fiberRefMap.clear();
2416
2836
  const topLevelNodes = walkFiber(rootFiber.child, "");
2417
- debugLog(
2418
- "[FloTrace] walkFiber found",
2419
- topLevelNodes.length,
2420
- "top-level nodes"
2421
- );
2837
+ debugLog("[FloTrace] walkFiber found", topLevelNodes.length, "top-level nodes");
2422
2838
  if (topLevelNodes.length === 1) {
2423
2839
  return topLevelNodes[0];
2424
2840
  }
@@ -2485,9 +2901,7 @@ function findFiberRootFromDOM() {
2485
2901
  }
2486
2902
  }
2487
2903
  }
2488
- console.warn(
2489
- "[FloTrace] Could not find React fiber root from any DOM element"
2490
- );
2904
+ console.warn("[FloTrace] Could not find React fiber root from any DOM element");
2491
2905
  return null;
2492
2906
  } catch (error) {
2493
2907
  console.error("[FloTrace] Error finding fiber root from DOM:", error);
@@ -2549,9 +2963,7 @@ function executeSnapshot(root) {
2549
2963
  adaptSnapshotInterval(nodeCount);
2550
2964
  const client2 = getWebSocketClient();
2551
2965
  if (!client2.connected) {
2552
- console.warn(
2553
- "[FloTrace] WebSocket not connected, cannot send tree snapshot"
2554
- );
2966
+ console.warn("[FloTrace] WebSocket not connected, cannot send tree snapshot");
2555
2967
  return;
2556
2968
  }
2557
2969
  const currentFlatTree = flattenTree2(tree);
@@ -2567,6 +2979,7 @@ function executeSnapshot(root) {
2567
2979
  "nextInterval:",
2568
2980
  snapshotIntervalMs + "ms"
2569
2981
  );
2982
+ logTreeSnapshot(tree, `send seq=${snapshotCounter}`);
2570
2983
  client2.sendImmediate({
2571
2984
  type: "runtime:treeSnapshot",
2572
2985
  tree,
@@ -2587,6 +3000,7 @@ function executeSnapshot(root) {
2587
3000
  "updated:",
2588
3001
  diff.updated.length
2589
3002
  );
3003
+ logTreeSummary(tree, `diff seq=${diffSeq}`);
2590
3004
  client2.sendImmediate({
2591
3005
  type: "runtime:treeDiff",
2592
3006
  seq: diffSeq,
@@ -2727,9 +3141,7 @@ function installFiberTreeWalker(options = {}) {
2727
3141
  return () => uninstallFiberTreeWalker();
2728
3142
  }
2729
3143
  if (typeof window === "undefined") {
2730
- console.warn(
2731
- "[FloTrace] Not in browser environment, cannot install fiber tree walker"
2732
- );
3144
+ console.warn("[FloTrace] Not in browser environment, cannot install fiber tree walker");
2733
3145
  return () => {
2734
3146
  };
2735
3147
  }
@@ -2756,10 +3168,7 @@ function installFiberTreeWalker(options = {}) {
2756
3168
  try {
2757
3169
  originalOnCommitFiberRoot(rendererID, root, priority);
2758
3170
  } catch (error) {
2759
- console.error(
2760
- "[FloTrace] Error in original onCommitFiberRoot:",
2761
- error
2762
- );
3171
+ console.error("[FloTrace] Error in original onCommitFiberRoot:", error);
2763
3172
  }
2764
3173
  }
2765
3174
  if (hookedRendererID === null) {
@@ -2785,9 +3194,7 @@ function installFiberTreeWalker(options = {}) {
2785
3194
  scheduleSnapshot(root);
2786
3195
  };
2787
3196
  activeStrategy = "devtools";
2788
- console.log(
2789
- "[FloTrace] Fiber tree walker installed (DevTools hook strategy)"
2790
- );
3197
+ console.log("[FloTrace] Fiber tree walker installed (DevTools hook strategy)");
2791
3198
  setTimeout(() => {
2792
3199
  try {
2793
3200
  const root = findFiberRootFromDOM();
@@ -2800,9 +3207,7 @@ function installFiberTreeWalker(options = {}) {
2800
3207
  }, 100);
2801
3208
  } else {
2802
3209
  activeStrategy = "dom";
2803
- console.log(
2804
- "[FloTrace] Fiber tree walker installed (DOM fallback strategy)"
2805
- );
3210
+ console.log("[FloTrace] Fiber tree walker installed (DOM fallback strategy)");
2806
3211
  setTimeout(() => {
2807
3212
  try {
2808
3213
  const root = findFiberRootFromDOM();
@@ -3080,12 +3485,18 @@ function installZustandTracker(stores, client2) {
3080
3485
  try {
3081
3486
  scheduleStoreUpdate(storeName, prevState, newState, client2);
3082
3487
  } catch (error) {
3083
- console.error(`[FloTrace] Error in Zustand subscribe callback for "${storeName}":`, error);
3488
+ console.error(
3489
+ `[FloTrace] Error in Zustand subscribe callback for "${storeName}":`,
3490
+ error
3491
+ );
3084
3492
  }
3085
3493
  });
3086
3494
  activeUnsubscribers.push(unsubscribe);
3087
3495
  } catch (error) {
3088
- console.error(`[FloTrace] Failed to install tracker for Zustand store "${storeName}":`, error);
3496
+ console.error(
3497
+ `[FloTrace] Failed to install tracker for Zustand store "${storeName}":`,
3498
+ error
3499
+ );
3089
3500
  }
3090
3501
  }
3091
3502
  }
@@ -3128,10 +3539,13 @@ function scheduleStoreUpdate(storeName, prevState, newState, client2) {
3128
3539
  if (changedKeys.length === 0) return;
3129
3540
  const existing = debounceTimers.get(storeName);
3130
3541
  if (existing) clearTimeout(existing);
3131
- debounceTimers.set(storeName, setTimeout(() => {
3132
- debounceTimers.delete(storeName);
3133
- sendStoreUpdate(storeName, newState, changedKeys, client2);
3134
- }, DEBOUNCE_MS));
3542
+ debounceTimers.set(
3543
+ storeName,
3544
+ setTimeout(() => {
3545
+ debounceTimers.delete(storeName);
3546
+ sendStoreUpdate(storeName, newState, changedKeys, client2);
3547
+ }, DEBOUNCE_MS)
3548
+ );
3135
3549
  }
3136
3550
  function sendStoreUpdate(storeName, state, changedKeys, client2) {
3137
3551
  try {
@@ -3648,7 +4062,6 @@ function safeCall(fn, fallback) {
3648
4062
 
3649
4063
  // src/valueTraceResolver.ts
3650
4064
  var FIBER_TAG_CONTEXT_PROVIDER = 10;
3651
- var BUDGET_MS = 100;
3652
4065
  var SCAN_DEPTH = 3;
3653
4066
  var MAX_PROP_CHAIN_DEPTH = 30;
3654
4067
  function now() {
@@ -3695,8 +4108,7 @@ function findReferenceMatchAtTopLevel(target, container) {
3695
4108
  }
3696
4109
  return null;
3697
4110
  }
3698
- function findMatchingPathInObject(target, targetFp, container, currentPath, depth, deadline, cache) {
3699
- if (now() > deadline) return null;
4111
+ function findMatchingPathInObject(target, targetFp, container, currentPath, depth, cache) {
3700
4112
  if (depth > SCAN_DEPTH) return null;
3701
4113
  if (container === null || typeof container !== "object") return null;
3702
4114
  const selfMatch = valuesMatch(target, targetFp, container, cache);
@@ -3706,7 +4118,14 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
3706
4118
  const child = container[i];
3707
4119
  const directMatch = valuesMatch(target, targetFp, child, cache);
3708
4120
  if (directMatch) return { path: [...currentPath, String(i)], confidence: directMatch };
3709
- const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, String(i)], depth + 1, deadline, cache);
4121
+ const nested = findMatchingPathInObject(
4122
+ target,
4123
+ targetFp,
4124
+ child,
4125
+ [...currentPath, String(i)],
4126
+ depth + 1,
4127
+ cache
4128
+ );
3710
4129
  if (nested) return nested;
3711
4130
  }
3712
4131
  } else {
@@ -3714,7 +4133,14 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
3714
4133
  const child = container[key];
3715
4134
  const directMatch = valuesMatch(target, targetFp, child, cache);
3716
4135
  if (directMatch) return { path: [...currentPath, key], confidence: directMatch };
3717
- const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, key], depth + 1, deadline, cache);
4136
+ const nested = findMatchingPathInObject(
4137
+ target,
4138
+ targetFp,
4139
+ child,
4140
+ [...currentPath, key],
4141
+ depth + 1,
4142
+ cache
4143
+ );
3718
4144
  if (nested) return nested;
3719
4145
  }
3720
4146
  }
@@ -3781,8 +4207,6 @@ function resolveOriginViaTagOrKeyPath(matchedValue, stateRoot, keyPath) {
3781
4207
  return findFetchOrigin(matchedValue, { ignoreTTL: true }) ?? findFetchOriginUpKeyPath(stateRoot, keyPath);
3782
4208
  }
3783
4209
  function resolveValueTrace(input) {
3784
- const startedAt = now();
3785
- const deadline = startedAt + BUDGET_MS;
3786
4210
  const steps = [];
3787
4211
  const base = {
3788
4212
  rootNodeId: input.nodeId,
@@ -3821,7 +4245,12 @@ function resolveValueTrace(input) {
3821
4245
  nodeId: input.nodeId,
3822
4246
  componentName: rootComponentName,
3823
4247
  propPath: input.propPath,
3824
- confidence: "exact"
4248
+ confidence: "exact",
4249
+ // P6: `fiber.memoizedProps[FLOTRACE_SOURCE]` is the JSX call site where
4250
+ // this fiber was created — i.e., the `<Consumer .../>` JSX in the
4251
+ // PARENT's source file. That's exactly the "drilled from `Parent.tsx:42`"
4252
+ // attribution the user wants on the leaf prop step.
4253
+ callSiteOfParentJsx: readJsxSourceFromFiber(fiber)
3825
4254
  });
3826
4255
  } else if (input.hookPath) {
3827
4256
  steps.push({
@@ -3839,7 +4268,6 @@ function resolveValueTrace(input) {
3839
4268
  let current = fiber.return;
3840
4269
  let hops = 0;
3841
4270
  while (current && hops < MAX_PROP_CHAIN_DEPTH) {
3842
- if (now() > deadline) return { ...base, steps, truncated: true, resolvedAtMs: now() };
3843
4271
  if (current.tag !== FIBER_TAG_CONTEXT_PROVIDER) {
3844
4272
  const props = current.memoizedProps;
3845
4273
  if (props) {
@@ -3847,7 +4275,7 @@ function resolveValueTrace(input) {
3847
4275
  let matchPath = refKey !== null ? [refKey] : null;
3848
4276
  let matchConfidence = "exact";
3849
4277
  if (matchPath === null) {
3850
- const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0, deadline, fpCache);
4278
+ const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0, fpCache);
3851
4279
  if (match) {
3852
4280
  matchPath = match.path;
3853
4281
  matchConfidence = match.confidence;
@@ -3862,7 +4290,12 @@ function resolveValueTrace(input) {
3862
4290
  nodeId: ancestorNodeId,
3863
4291
  componentName: ancestorName,
3864
4292
  propPath: trailingSubPath.length > 0 ? [...matchPath, ...trailingSubPath] : matchPath,
3865
- confidence: matchConfidence
4293
+ confidence: matchConfidence,
4294
+ // P6: attribute the parent JSX call site that drilled this
4295
+ // prop. When the user opted into the JSX runtime AND this
4296
+ // ancestor's fiber went through jsxDEV, the consumer sees
4297
+ // "drilled from `Parent.tsx:42:8`" with a click-to-IDE link.
4298
+ callSiteOfParentJsx: readJsxSourceFromFiber(current)
3866
4299
  });
3867
4300
  }
3868
4301
  } else {
@@ -3911,7 +4344,11 @@ function resolveValueTrace(input) {
3911
4344
  const contextMatch = findContextMatch(fiber, rootValue, rootFp, fiberToNodeId, fpCache);
3912
4345
  if (contextMatch) {
3913
4346
  steps.push(contextMatch.step);
3914
- const providerStoreMatch = findStoreMatch(contextMatch.providerValue, cachedFp(contextMatch.providerValue, fpCache), deadline, fpCache);
4347
+ const providerStoreMatch = findStoreMatch(
4348
+ contextMatch.providerValue,
4349
+ cachedFp(contextMatch.providerValue, fpCache),
4350
+ fpCache
4351
+ );
3915
4352
  if (providerStoreMatch) {
3916
4353
  steps.push({
3917
4354
  kind: "store",
@@ -3936,7 +4373,7 @@ function resolveValueTrace(input) {
3936
4373
  }
3937
4374
  return { ...base, steps, resolvedAtMs: now() };
3938
4375
  }
3939
- const storeMatch = findStoreMatch(rootValue, rootFp, deadline, fpCache);
4376
+ const storeMatch = findStoreMatch(rootValue, rootFp, fpCache);
3940
4377
  if (storeMatch) {
3941
4378
  steps.push({
3942
4379
  kind: "store",
@@ -4041,10 +4478,9 @@ function findNearestProvider(consumer, contextObj) {
4041
4478
  }
4042
4479
  return null;
4043
4480
  }
4044
- function findStoreMatch(target, targetFp, deadline, cache) {
4481
+ function findStoreMatch(target, targetFp, cache) {
4045
4482
  for (const [storeName, state] of getZustandSnapshot()) {
4046
- if (now() > deadline) return null;
4047
- const hit = findMatchingPathInObject(target, targetFp, state, [], 0, deadline, cache);
4483
+ const hit = findMatchingPathInObject(target, targetFp, state, [], 0, cache);
4048
4484
  if (hit) {
4049
4485
  return {
4050
4486
  source: "zustand",
@@ -4058,8 +4494,7 @@ function findStoreMatch(target, targetFp, deadline, cache) {
4058
4494
  }
4059
4495
  const redux = getReduxSnapshot();
4060
4496
  if (redux) {
4061
- if (now() > deadline) return null;
4062
- const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, deadline, cache);
4497
+ const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, cache);
4063
4498
  if (hit) {
4064
4499
  return {
4065
4500
  source: "redux",
@@ -4072,8 +4507,7 @@ function findStoreMatch(target, targetFp, deadline, cache) {
4072
4507
  }
4073
4508
  }
4074
4509
  for (const [queryHash, entry] of getTanstackSnapshot()) {
4075
- if (now() > deadline) return null;
4076
- const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, deadline, cache);
4510
+ const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, cache);
4077
4511
  if (hit) {
4078
4512
  return {
4079
4513
  source: "tanstack-query",
@@ -4107,13 +4541,22 @@ function detectWebFramework() {
4107
4541
  }
4108
4542
  export {
4109
4543
  DEFAULT_CONFIG,
4544
+ FLOTRACE_SOURCE,
4110
4545
  FloTraceWebSocketClient,
4546
+ JSX_RUNTIME_ACTIVE_KEY,
4111
4547
  buildAncestorChain,
4548
+ clearCallSiteRenders,
4112
4549
  clearFetchOriginTags,
4550
+ computeCallSiteId,
4551
+ computeCallSiteMetricsPayload,
4552
+ describeFiberType,
4553
+ detectInlineLiterals,
4113
4554
  detectServerComponent,
4114
4555
  detectWebFramework,
4115
4556
  disposeWebSocketClient,
4116
4557
  findFetchOrigin,
4558
+ getCallSiteRenderRate,
4559
+ getCallSiteRenders,
4117
4560
  getChangedKeys,
4118
4561
  getComponentNameFromFiber,
4119
4562
  getCurrentRenderingFiber,
@@ -4136,9 +4579,15 @@ export {
4136
4579
  installTanStackQueryTracker,
4137
4580
  installTimelineTracker,
4138
4581
  installZustandTracker,
4582
+ isJsxRuntimeActive,
4139
4583
  isReduxStore,
4140
4584
  isTanStackQueryClient,
4585
+ logTreeSnapshot,
4586
+ logTreeSummary,
4587
+ markJsxRuntimeActive,
4141
4588
  maybeEmitNextjsContext,
4589
+ normalizeJsxSourcePath,
4590
+ recordCallSiteRender,
4142
4591
  recordTimelineEvent,
4143
4592
  requestFullSnapshot,
4144
4593
  requestTreeSnapshot,
@@ -4146,6 +4595,8 @@ export {
4146
4595
  resolveValueTrace,
4147
4596
  serializeProps,
4148
4597
  serializeValue,
4598
+ setDuplicateKeyEmitter,
4599
+ setFiberDebug,
4149
4600
  tagFetchData,
4150
4601
  uninstallFiberTreeWalker,
4151
4602
  uninstallReduxTracker,