@flotrace/runtime-core 2.2.4 → 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/README.md +94 -0
- package/dist/chunk-QLOJU5F2.mjs +181 -0
- package/dist/index.d.mts +377 -17
- package/dist/index.d.ts +377 -17
- package/dist/index.js +674 -91
- package/dist/index.mjs +550 -91
- package/dist/jsx-dev-runtime.d.mts +64 -0
- package/dist/jsx-dev-runtime.d.ts +64 -0
- package/dist/jsx-dev-runtime.js +179 -0
- package/dist/jsx-dev-runtime.mjs +46 -0
- package/dist/jsx-runtime.d.mts +1 -0
- package/dist/jsx-runtime.d.ts +1 -0
- package/dist/jsx-runtime.js +34 -0
- package/dist/jsx-runtime.mjs +7 -0
- package/package.json +12 -1
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
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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 = {
|
|
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([
|
|
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
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
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({
|
|
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({
|
|
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({
|
|
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(
|
|
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)
|
|
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 ? [
|
|
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(
|
|
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))
|
|
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(
|
|
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(
|
|
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(
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
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 {
|
|
@@ -3704,7 +4118,14 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
|
|
|
3704
4118
|
const child = container[i];
|
|
3705
4119
|
const directMatch = valuesMatch(target, targetFp, child, cache);
|
|
3706
4120
|
if (directMatch) return { path: [...currentPath, String(i)], confidence: directMatch };
|
|
3707
|
-
const nested = findMatchingPathInObject(
|
|
4121
|
+
const nested = findMatchingPathInObject(
|
|
4122
|
+
target,
|
|
4123
|
+
targetFp,
|
|
4124
|
+
child,
|
|
4125
|
+
[...currentPath, String(i)],
|
|
4126
|
+
depth + 1,
|
|
4127
|
+
cache
|
|
4128
|
+
);
|
|
3708
4129
|
if (nested) return nested;
|
|
3709
4130
|
}
|
|
3710
4131
|
} else {
|
|
@@ -3712,7 +4133,14 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
|
|
|
3712
4133
|
const child = container[key];
|
|
3713
4134
|
const directMatch = valuesMatch(target, targetFp, child, cache);
|
|
3714
4135
|
if (directMatch) return { path: [...currentPath, key], confidence: directMatch };
|
|
3715
|
-
const nested = findMatchingPathInObject(
|
|
4136
|
+
const nested = findMatchingPathInObject(
|
|
4137
|
+
target,
|
|
4138
|
+
targetFp,
|
|
4139
|
+
child,
|
|
4140
|
+
[...currentPath, key],
|
|
4141
|
+
depth + 1,
|
|
4142
|
+
cache
|
|
4143
|
+
);
|
|
3716
4144
|
if (nested) return nested;
|
|
3717
4145
|
}
|
|
3718
4146
|
}
|
|
@@ -3817,7 +4245,12 @@ function resolveValueTrace(input) {
|
|
|
3817
4245
|
nodeId: input.nodeId,
|
|
3818
4246
|
componentName: rootComponentName,
|
|
3819
4247
|
propPath: input.propPath,
|
|
3820
|
-
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)
|
|
3821
4254
|
});
|
|
3822
4255
|
} else if (input.hookPath) {
|
|
3823
4256
|
steps.push({
|
|
@@ -3857,7 +4290,12 @@ function resolveValueTrace(input) {
|
|
|
3857
4290
|
nodeId: ancestorNodeId,
|
|
3858
4291
|
componentName: ancestorName,
|
|
3859
4292
|
propPath: trailingSubPath.length > 0 ? [...matchPath, ...trailingSubPath] : matchPath,
|
|
3860
|
-
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)
|
|
3861
4299
|
});
|
|
3862
4300
|
}
|
|
3863
4301
|
} else {
|
|
@@ -3906,7 +4344,11 @@ function resolveValueTrace(input) {
|
|
|
3906
4344
|
const contextMatch = findContextMatch(fiber, rootValue, rootFp, fiberToNodeId, fpCache);
|
|
3907
4345
|
if (contextMatch) {
|
|
3908
4346
|
steps.push(contextMatch.step);
|
|
3909
|
-
const providerStoreMatch = findStoreMatch(
|
|
4347
|
+
const providerStoreMatch = findStoreMatch(
|
|
4348
|
+
contextMatch.providerValue,
|
|
4349
|
+
cachedFp(contextMatch.providerValue, fpCache),
|
|
4350
|
+
fpCache
|
|
4351
|
+
);
|
|
3910
4352
|
if (providerStoreMatch) {
|
|
3911
4353
|
steps.push({
|
|
3912
4354
|
kind: "store",
|
|
@@ -4099,13 +4541,22 @@ function detectWebFramework() {
|
|
|
4099
4541
|
}
|
|
4100
4542
|
export {
|
|
4101
4543
|
DEFAULT_CONFIG,
|
|
4544
|
+
FLOTRACE_SOURCE,
|
|
4102
4545
|
FloTraceWebSocketClient,
|
|
4546
|
+
JSX_RUNTIME_ACTIVE_KEY,
|
|
4103
4547
|
buildAncestorChain,
|
|
4548
|
+
clearCallSiteRenders,
|
|
4104
4549
|
clearFetchOriginTags,
|
|
4550
|
+
computeCallSiteId,
|
|
4551
|
+
computeCallSiteMetricsPayload,
|
|
4552
|
+
describeFiberType,
|
|
4553
|
+
detectInlineLiterals,
|
|
4105
4554
|
detectServerComponent,
|
|
4106
4555
|
detectWebFramework,
|
|
4107
4556
|
disposeWebSocketClient,
|
|
4108
4557
|
findFetchOrigin,
|
|
4558
|
+
getCallSiteRenderRate,
|
|
4559
|
+
getCallSiteRenders,
|
|
4109
4560
|
getChangedKeys,
|
|
4110
4561
|
getComponentNameFromFiber,
|
|
4111
4562
|
getCurrentRenderingFiber,
|
|
@@ -4128,9 +4579,15 @@ export {
|
|
|
4128
4579
|
installTanStackQueryTracker,
|
|
4129
4580
|
installTimelineTracker,
|
|
4130
4581
|
installZustandTracker,
|
|
4582
|
+
isJsxRuntimeActive,
|
|
4131
4583
|
isReduxStore,
|
|
4132
4584
|
isTanStackQueryClient,
|
|
4585
|
+
logTreeSnapshot,
|
|
4586
|
+
logTreeSummary,
|
|
4587
|
+
markJsxRuntimeActive,
|
|
4133
4588
|
maybeEmitNextjsContext,
|
|
4589
|
+
normalizeJsxSourcePath,
|
|
4590
|
+
recordCallSiteRender,
|
|
4134
4591
|
recordTimelineEvent,
|
|
4135
4592
|
requestFullSnapshot,
|
|
4136
4593
|
requestTreeSnapshot,
|
|
@@ -4138,6 +4595,8 @@ export {
|
|
|
4138
4595
|
resolveValueTrace,
|
|
4139
4596
|
serializeProps,
|
|
4140
4597
|
serializeValue,
|
|
4598
|
+
setDuplicateKeyEmitter,
|
|
4599
|
+
setFiberDebug,
|
|
4141
4600
|
tagFetchData,
|
|
4142
4601
|
uninstallFiberTreeWalker,
|
|
4143
4602
|
uninstallReduxTracker,
|