@flotrace/runtime-core 2.0.1 → 2.2.1
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.d.mts +173 -2
- package/dist/index.d.ts +173 -2
- package/dist/index.js +370 -8
- package/dist/index.mjs +366 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46,8 +46,11 @@ __export(index_exports, {
|
|
|
46
46
|
getNodeEffects: () => getNodeEffects,
|
|
47
47
|
getNodeHooks: () => getNodeHooks,
|
|
48
48
|
getNodeProps: () => getNodeProps,
|
|
49
|
+
getReduxSnapshot: () => getReduxSnapshot,
|
|
50
|
+
getTanstackSnapshot: () => getTanstackSnapshot,
|
|
49
51
|
getTimeline: () => getTimeline,
|
|
50
52
|
getWebSocketClient: () => getWebSocketClient,
|
|
53
|
+
getZustandSnapshot: () => getZustandSnapshot,
|
|
51
54
|
hasActiveTags: () => hasActiveTags,
|
|
52
55
|
inspectEffects: () => inspectEffects,
|
|
53
56
|
inspectHooks: () => inspectHooks,
|
|
@@ -64,6 +67,7 @@ __export(index_exports, {
|
|
|
64
67
|
requestFullSnapshot: () => requestFullSnapshot,
|
|
65
68
|
requestTreeSnapshot: () => requestTreeSnapshot,
|
|
66
69
|
resetNextjsDetection: () => resetNextjsDetection,
|
|
70
|
+
resolveValueTrace: () => resolveValueTrace,
|
|
67
71
|
serializeProps: () => serializeProps,
|
|
68
72
|
serializeValue: () => serializeValue,
|
|
69
73
|
tagFetchData: () => tagFetchData,
|
|
@@ -1629,8 +1633,8 @@ function runAnalysis(tree, fiberRefMap2) {
|
|
|
1629
1633
|
}
|
|
1630
1634
|
function schedulePropDrillingAnalysis(tree, fiberRefMap2, client2) {
|
|
1631
1635
|
if (analyzeTimer) clearTimeout(analyzeTimer);
|
|
1632
|
-
const
|
|
1633
|
-
const elapsed =
|
|
1636
|
+
const now2 = Date.now();
|
|
1637
|
+
const elapsed = now2 - lastAnalysisTime;
|
|
1634
1638
|
const delay = elapsed >= ANALYZE_INTERVAL_MS ? 0 : ANALYZE_INTERVAL_MS - elapsed;
|
|
1635
1639
|
analyzeTimer = setTimeout(() => {
|
|
1636
1640
|
analyzeTimer = null;
|
|
@@ -2657,7 +2661,7 @@ function executeSnapshot(root) {
|
|
|
2657
2661
|
}
|
|
2658
2662
|
previousFlatTree = currentFlatTree;
|
|
2659
2663
|
if (pendingLocalStateCorrelations.length > 0) {
|
|
2660
|
-
const
|
|
2664
|
+
const now2 = Date.now();
|
|
2661
2665
|
const toSend = pendingLocalStateCorrelations.splice(0);
|
|
2662
2666
|
for (const corr of toSend) {
|
|
2663
2667
|
try {
|
|
@@ -2666,7 +2670,7 @@ function executeSnapshot(root) {
|
|
|
2666
2670
|
requestId: corr.requestId,
|
|
2667
2671
|
componentName: corr.componentName,
|
|
2668
2672
|
hookIndex: corr.hookIndex,
|
|
2669
|
-
timestamp:
|
|
2673
|
+
timestamp: now2
|
|
2670
2674
|
});
|
|
2671
2675
|
} catch {
|
|
2672
2676
|
}
|
|
@@ -3111,6 +3115,7 @@ var activeUnsubscribers = [];
|
|
|
3111
3115
|
var isInstalled4 = false;
|
|
3112
3116
|
var debounceTimers = /* @__PURE__ */ new Map();
|
|
3113
3117
|
var DEBOUNCE_MS = 200;
|
|
3118
|
+
var trackedStores = /* @__PURE__ */ new Map();
|
|
3114
3119
|
function installZustandTracker(stores, client2) {
|
|
3115
3120
|
if (isInstalled4) {
|
|
3116
3121
|
console.warn("[FloTrace] Zustand tracker already installed, reinstalling");
|
|
@@ -3126,6 +3131,7 @@ function installZustandTracker(stores, client2) {
|
|
|
3126
3131
|
continue;
|
|
3127
3132
|
}
|
|
3128
3133
|
try {
|
|
3134
|
+
trackedStores.set(storeName, store);
|
|
3129
3135
|
const initialState = store.getState();
|
|
3130
3136
|
sendStoreUpdate(storeName, initialState, Object.keys(initialState), client2);
|
|
3131
3137
|
const unsubscribe = store.subscribe((newState, prevState) => {
|
|
@@ -3155,9 +3161,20 @@ function uninstallZustandTracker() {
|
|
|
3155
3161
|
}
|
|
3156
3162
|
}
|
|
3157
3163
|
activeUnsubscribers = [];
|
|
3164
|
+
trackedStores.clear();
|
|
3158
3165
|
isInstalled4 = false;
|
|
3159
3166
|
console.log("[FloTrace] Zustand tracker uninstalled");
|
|
3160
3167
|
}
|
|
3168
|
+
function getZustandSnapshot() {
|
|
3169
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
3170
|
+
for (const [name, store] of trackedStores) {
|
|
3171
|
+
try {
|
|
3172
|
+
snapshot.set(name, store.getState());
|
|
3173
|
+
} catch {
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
return snapshot;
|
|
3177
|
+
}
|
|
3161
3178
|
function scheduleStoreUpdate(storeName, prevState, newState, client2) {
|
|
3162
3179
|
let changedKeys;
|
|
3163
3180
|
try {
|
|
@@ -3196,6 +3213,7 @@ var isInstalled5 = false;
|
|
|
3196
3213
|
var debounceTimer = null;
|
|
3197
3214
|
var previousState = null;
|
|
3198
3215
|
var DEBOUNCE_MS2 = 200;
|
|
3216
|
+
var trackedStore = null;
|
|
3199
3217
|
function isReduxStore(obj) {
|
|
3200
3218
|
return typeof obj === "object" && obj !== null && typeof obj.getState === "function" && typeof obj.subscribe === "function" && typeof obj.dispatch === "function";
|
|
3201
3219
|
}
|
|
@@ -3207,6 +3225,7 @@ function installReduxTracker(store, client2) {
|
|
|
3207
3225
|
isInstalled5 = true;
|
|
3208
3226
|
console.log("[FloTrace] Installing Redux tracker");
|
|
3209
3227
|
try {
|
|
3228
|
+
trackedStore = store;
|
|
3210
3229
|
const initialState = store.getState();
|
|
3211
3230
|
previousState = initialState;
|
|
3212
3231
|
sendReduxUpdate(initialState, Object.keys(initialState), client2);
|
|
@@ -3238,9 +3257,18 @@ function uninstallReduxTracker() {
|
|
|
3238
3257
|
activeUnsubscribe = null;
|
|
3239
3258
|
}
|
|
3240
3259
|
previousState = null;
|
|
3260
|
+
trackedStore = null;
|
|
3241
3261
|
isInstalled5 = false;
|
|
3242
3262
|
console.log("[FloTrace] Redux tracker uninstalled");
|
|
3243
3263
|
}
|
|
3264
|
+
function getReduxSnapshot() {
|
|
3265
|
+
if (!trackedStore) return null;
|
|
3266
|
+
try {
|
|
3267
|
+
return trackedStore.getState();
|
|
3268
|
+
} catch {
|
|
3269
|
+
return null;
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3244
3272
|
function scheduleReduxUpdate(newState, client2) {
|
|
3245
3273
|
let changedKeys;
|
|
3246
3274
|
try {
|
|
@@ -3278,6 +3306,7 @@ var queryUnsubscribe = null;
|
|
|
3278
3306
|
var mutationUnsubscribe = null;
|
|
3279
3307
|
var debounceTimer2 = null;
|
|
3280
3308
|
var DEBOUNCE_MS3 = 300;
|
|
3309
|
+
var trackedClient = null;
|
|
3281
3310
|
var MAX_EVENTS_PER_QUERY = 50;
|
|
3282
3311
|
var queryTracking = /* @__PURE__ */ new Map();
|
|
3283
3312
|
var CORRELATION_WINDOW_MS = 500;
|
|
@@ -3300,6 +3329,7 @@ function installTanStackQueryTracker(queryClient, client2) {
|
|
|
3300
3329
|
isInstalled6 = true;
|
|
3301
3330
|
console.log("[FloTrace] Installing TanStack Query tracker");
|
|
3302
3331
|
try {
|
|
3332
|
+
trackedClient = queryClient;
|
|
3303
3333
|
const queryCache = queryClient.getQueryCache();
|
|
3304
3334
|
const mutationCache = queryClient.getMutationCache();
|
|
3305
3335
|
for (const query of queryCache.getAll()) {
|
|
@@ -3364,9 +3394,24 @@ function uninstallTanStackQueryTracker() {
|
|
|
3364
3394
|
clearTimeout(pending.timeoutId);
|
|
3365
3395
|
}
|
|
3366
3396
|
pendingCorrelations.clear();
|
|
3397
|
+
trackedClient = null;
|
|
3367
3398
|
isInstalled6 = false;
|
|
3368
3399
|
console.log("[FloTrace] TanStack Query tracker uninstalled");
|
|
3369
3400
|
}
|
|
3401
|
+
function getTanstackSnapshot() {
|
|
3402
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
3403
|
+
if (!trackedClient) return snapshot;
|
|
3404
|
+
try {
|
|
3405
|
+
const queryCache = trackedClient.getQueryCache();
|
|
3406
|
+
for (const query of queryCache.getAll()) {
|
|
3407
|
+
if (query.state.data !== void 0) {
|
|
3408
|
+
snapshot.set(query.queryHash, { queryKey: query.queryKey, data: query.state.data });
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
} catch {
|
|
3412
|
+
}
|
|
3413
|
+
return snapshot;
|
|
3414
|
+
}
|
|
3370
3415
|
function computeDataHash(data) {
|
|
3371
3416
|
if (data === null || data === void 0) return "__null__";
|
|
3372
3417
|
try {
|
|
@@ -3429,11 +3474,11 @@ function updateQueryTracking(query, eventType) {
|
|
|
3429
3474
|
}
|
|
3430
3475
|
}
|
|
3431
3476
|
if (tracking.prevFetchStatus === "idle" && currentFetchStatus === "fetching") {
|
|
3432
|
-
const
|
|
3477
|
+
const now2 = Date.now();
|
|
3433
3478
|
for (const pending of pendingCorrelations.values()) {
|
|
3434
3479
|
if (pending.idleQueryHashes.has(query.queryHash)) {
|
|
3435
3480
|
pending.affectedQueries.set(query.queryHash, {
|
|
3436
|
-
fetchStartedAt:
|
|
3481
|
+
fetchStartedAt: now2,
|
|
3437
3482
|
queryKey: query.queryKey
|
|
3438
3483
|
});
|
|
3439
3484
|
}
|
|
@@ -3445,7 +3490,7 @@ function updateQueryTracking(query, eventType) {
|
|
|
3445
3490
|
}
|
|
3446
3491
|
function openCorrelationWindow(mutation, queryCache, mutationCache, client2) {
|
|
3447
3492
|
const correlationId = `corr-${++correlationCounter}`;
|
|
3448
|
-
const
|
|
3493
|
+
const now2 = Date.now();
|
|
3449
3494
|
const idleQueryHashes = /* @__PURE__ */ new Set();
|
|
3450
3495
|
for (const query of queryCache.getAll()) {
|
|
3451
3496
|
if (query.state.fetchStatus === "idle") {
|
|
@@ -3459,7 +3504,7 @@ function openCorrelationWindow(mutation, queryCache, mutationCache, client2) {
|
|
|
3459
3504
|
correlationId,
|
|
3460
3505
|
mutationId: mutation.mutationId,
|
|
3461
3506
|
mutationKey: mutation.options.mutationKey,
|
|
3462
|
-
completedAt:
|
|
3507
|
+
completedAt: now2,
|
|
3463
3508
|
idleQueryHashes,
|
|
3464
3509
|
affectedQueries: /* @__PURE__ */ new Map(),
|
|
3465
3510
|
timeoutId
|
|
@@ -3659,6 +3704,319 @@ function safeCall(fn, fallback) {
|
|
|
3659
3704
|
}
|
|
3660
3705
|
}
|
|
3661
3706
|
|
|
3707
|
+
// src/valueTraceResolver.ts
|
|
3708
|
+
var FIBER_TAG_CONTEXT_PROVIDER = 10;
|
|
3709
|
+
var BUDGET_MS = 50;
|
|
3710
|
+
var SCAN_DEPTH = 3;
|
|
3711
|
+
var MAX_PROP_CHAIN_DEPTH = 30;
|
|
3712
|
+
function now() {
|
|
3713
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
3714
|
+
}
|
|
3715
|
+
function walkPath(root, path) {
|
|
3716
|
+
let cur = root;
|
|
3717
|
+
for (const key of path) {
|
|
3718
|
+
if (cur === null || cur === void 0 || typeof cur !== "object") return void 0;
|
|
3719
|
+
cur = cur[key];
|
|
3720
|
+
}
|
|
3721
|
+
return cur;
|
|
3722
|
+
}
|
|
3723
|
+
function getHookValueAt(fiber, hookIndex) {
|
|
3724
|
+
let hook = fiber.memoizedState;
|
|
3725
|
+
let i = 0;
|
|
3726
|
+
while (hook && i < hookIndex) {
|
|
3727
|
+
hook = hook.next;
|
|
3728
|
+
i++;
|
|
3729
|
+
}
|
|
3730
|
+
if (!hook) return void 0;
|
|
3731
|
+
return hook.memoizedState;
|
|
3732
|
+
}
|
|
3733
|
+
function valuesMatch(target, targetFp, candidate) {
|
|
3734
|
+
const targetIsObject = target !== null && typeof target === "object";
|
|
3735
|
+
const candidateIsObject = candidate !== null && typeof candidate === "object";
|
|
3736
|
+
if (targetIsObject && candidateIsObject && target === candidate) return "exact";
|
|
3737
|
+
if (!shouldFlagRename(target) || !shouldFlagRename(candidate)) return null;
|
|
3738
|
+
if (valueFingerprint(candidate) === targetFp) return "fingerprint-match";
|
|
3739
|
+
return null;
|
|
3740
|
+
}
|
|
3741
|
+
function findMatchingPathInObject(target, targetFp, container, currentPath, depth, deadline) {
|
|
3742
|
+
if (now() > deadline) return null;
|
|
3743
|
+
if (depth > SCAN_DEPTH) return null;
|
|
3744
|
+
if (container === null || typeof container !== "object") return null;
|
|
3745
|
+
const selfMatch = valuesMatch(target, targetFp, container);
|
|
3746
|
+
if (selfMatch) return { path: [...currentPath], confidence: selfMatch };
|
|
3747
|
+
if (Array.isArray(container)) {
|
|
3748
|
+
for (let i = 0; i < Math.min(container.length, 50); i++) {
|
|
3749
|
+
const child = container[i];
|
|
3750
|
+
const directMatch = valuesMatch(target, targetFp, child);
|
|
3751
|
+
if (directMatch) return { path: [...currentPath, String(i)], confidence: directMatch };
|
|
3752
|
+
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, String(i)], depth + 1, deadline);
|
|
3753
|
+
if (nested) return nested;
|
|
3754
|
+
}
|
|
3755
|
+
} else {
|
|
3756
|
+
for (const key of Object.keys(container)) {
|
|
3757
|
+
const child = container[key];
|
|
3758
|
+
const directMatch = valuesMatch(target, targetFp, child);
|
|
3759
|
+
if (directMatch) return { path: [...currentPath, key], confidence: directMatch };
|
|
3760
|
+
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, key], depth + 1, deadline);
|
|
3761
|
+
if (nested) return nested;
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
return null;
|
|
3765
|
+
}
|
|
3766
|
+
function buildFiberToNodeIdMap() {
|
|
3767
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
3768
|
+
for (const [nodeId, fiber] of getFiberRefMap()) {
|
|
3769
|
+
reverse.set(fiber, nodeId);
|
|
3770
|
+
}
|
|
3771
|
+
return reverse;
|
|
3772
|
+
}
|
|
3773
|
+
function resolveValueTrace(input) {
|
|
3774
|
+
const startedAt = now();
|
|
3775
|
+
const deadline = startedAt + BUDGET_MS;
|
|
3776
|
+
const steps = [];
|
|
3777
|
+
const base = {
|
|
3778
|
+
rootNodeId: input.nodeId,
|
|
3779
|
+
rootPropPath: input.propPath,
|
|
3780
|
+
rootHookPath: input.hookPath,
|
|
3781
|
+
steps,
|
|
3782
|
+
resolvedAtMs: now()
|
|
3783
|
+
};
|
|
3784
|
+
const fiber = getFiberRefMap().get(input.nodeId);
|
|
3785
|
+
if (!fiber) {
|
|
3786
|
+
return { ...base, error: "no-fiber", resolvedAtMs: now() };
|
|
3787
|
+
}
|
|
3788
|
+
let rootValue;
|
|
3789
|
+
if (input.propPath && input.propPath.length > 0) {
|
|
3790
|
+
if (!fiber.memoizedProps) return { ...base, error: "value-not-found", resolvedAtMs: now() };
|
|
3791
|
+
rootValue = walkPath(fiber.memoizedProps, input.propPath);
|
|
3792
|
+
} else if (input.hookPath) {
|
|
3793
|
+
const hookValue = getHookValueAt(fiber, input.hookPath.hookIndex);
|
|
3794
|
+
rootValue = input.hookPath.subPath && input.hookPath.subPath.length > 0 ? walkPath(hookValue, input.hookPath.subPath) : hookValue;
|
|
3795
|
+
} else {
|
|
3796
|
+
return { ...base, error: "value-not-found", resolvedAtMs: now() };
|
|
3797
|
+
}
|
|
3798
|
+
if (rootValue === void 0) {
|
|
3799
|
+
return { ...base, error: "value-not-found", resolvedAtMs: now() };
|
|
3800
|
+
}
|
|
3801
|
+
const rootFp = valueFingerprint(rootValue);
|
|
3802
|
+
const fiberToNodeId = buildFiberToNodeIdMap();
|
|
3803
|
+
const rootComponentName = getComponentNameFromFiber(fiber) ?? "Unknown";
|
|
3804
|
+
if (input.propPath) {
|
|
3805
|
+
steps.push({
|
|
3806
|
+
kind: "prop",
|
|
3807
|
+
nodeId: input.nodeId,
|
|
3808
|
+
componentName: rootComponentName,
|
|
3809
|
+
propPath: input.propPath,
|
|
3810
|
+
confidence: "exact"
|
|
3811
|
+
});
|
|
3812
|
+
} else if (input.hookPath) {
|
|
3813
|
+
steps.push({
|
|
3814
|
+
kind: "hook-state",
|
|
3815
|
+
nodeId: input.nodeId,
|
|
3816
|
+
componentName: rootComponentName,
|
|
3817
|
+
hookIndex: input.hookPath.hookIndex,
|
|
3818
|
+
hookType: "unknown",
|
|
3819
|
+
// Cheap: full hook classification is expensive; caller can fetch separately.
|
|
3820
|
+
subPath: input.hookPath.subPath,
|
|
3821
|
+
confidence: "exact"
|
|
3822
|
+
});
|
|
3823
|
+
}
|
|
3824
|
+
if (input.propPath) {
|
|
3825
|
+
let current = fiber.return;
|
|
3826
|
+
let hops = 0;
|
|
3827
|
+
while (current && hops < MAX_PROP_CHAIN_DEPTH) {
|
|
3828
|
+
if (now() > deadline) return { ...base, steps, truncated: true, resolvedAtMs: now() };
|
|
3829
|
+
if (current.tag !== FIBER_TAG_CONTEXT_PROVIDER) {
|
|
3830
|
+
const props = current.memoizedProps;
|
|
3831
|
+
if (props) {
|
|
3832
|
+
const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0, deadline);
|
|
3833
|
+
if (match) {
|
|
3834
|
+
const ancestorNodeId = fiberToNodeId.get(current);
|
|
3835
|
+
const ancestorName = getComponentNameFromFiber(current) ?? "Unknown";
|
|
3836
|
+
if (ancestorNodeId) {
|
|
3837
|
+
steps.push({
|
|
3838
|
+
kind: "prop",
|
|
3839
|
+
nodeId: ancestorNodeId,
|
|
3840
|
+
componentName: ancestorName,
|
|
3841
|
+
propPath: match.path,
|
|
3842
|
+
confidence: match.confidence
|
|
3843
|
+
});
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
current = current.return;
|
|
3849
|
+
hops++;
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
if (input.hookPath) {
|
|
3853
|
+
const origin = findFetchOrigin(rootValue);
|
|
3854
|
+
if (origin) {
|
|
3855
|
+
steps.push({
|
|
3856
|
+
kind: "api",
|
|
3857
|
+
requestId: origin,
|
|
3858
|
+
method: "UNKNOWN",
|
|
3859
|
+
urlPath: "",
|
|
3860
|
+
ageMs: 0
|
|
3861
|
+
});
|
|
3862
|
+
return { ...base, steps, resolvedAtMs: now() };
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
const derivedMatch = findDerivationMatch(fiber, rootValue, rootFp, rootComponentName);
|
|
3866
|
+
if (derivedMatch) {
|
|
3867
|
+
steps.push({ ...derivedMatch, nodeId: input.nodeId });
|
|
3868
|
+
return { ...base, steps, resolvedAtMs: now() };
|
|
3869
|
+
}
|
|
3870
|
+
const contextMatch = findContextMatch(fiber, rootValue, rootFp, fiberToNodeId);
|
|
3871
|
+
if (contextMatch) {
|
|
3872
|
+
steps.push(contextMatch.step);
|
|
3873
|
+
const providerStoreMatch = findStoreMatch(contextMatch.providerValue, valueFingerprint(contextMatch.providerValue), deadline);
|
|
3874
|
+
if (providerStoreMatch) {
|
|
3875
|
+
steps.push({
|
|
3876
|
+
kind: "store",
|
|
3877
|
+
source: providerStoreMatch.source,
|
|
3878
|
+
storeName: providerStoreMatch.storeName,
|
|
3879
|
+
keyPath: providerStoreMatch.keyPath,
|
|
3880
|
+
confidence: providerStoreMatch.confidence
|
|
3881
|
+
});
|
|
3882
|
+
const origin = findFetchOrigin(providerStoreMatch.matchedValue);
|
|
3883
|
+
if (origin) {
|
|
3884
|
+
steps.push({ kind: "api", requestId: origin, method: "UNKNOWN", urlPath: "", ageMs: 0 });
|
|
3885
|
+
}
|
|
3886
|
+
} else {
|
|
3887
|
+
const origin = findFetchOrigin(contextMatch.providerValue);
|
|
3888
|
+
if (origin) {
|
|
3889
|
+
steps.push({ kind: "api", requestId: origin, method: "UNKNOWN", urlPath: "", ageMs: 0 });
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
return { ...base, steps, resolvedAtMs: now() };
|
|
3893
|
+
}
|
|
3894
|
+
const storeMatch = findStoreMatch(rootValue, rootFp, deadline);
|
|
3895
|
+
if (storeMatch) {
|
|
3896
|
+
steps.push({
|
|
3897
|
+
kind: "store",
|
|
3898
|
+
source: storeMatch.source,
|
|
3899
|
+
storeName: storeMatch.storeName,
|
|
3900
|
+
keyPath: storeMatch.keyPath,
|
|
3901
|
+
confidence: storeMatch.confidence
|
|
3902
|
+
});
|
|
3903
|
+
const origin = findFetchOrigin(storeMatch.matchedValue);
|
|
3904
|
+
if (origin) {
|
|
3905
|
+
steps.push({
|
|
3906
|
+
kind: "api",
|
|
3907
|
+
requestId: origin,
|
|
3908
|
+
method: "UNKNOWN",
|
|
3909
|
+
urlPath: "",
|
|
3910
|
+
ageMs: 0
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
return { ...base, steps, resolvedAtMs: now() };
|
|
3915
|
+
}
|
|
3916
|
+
function findContextMatch(consumer, target, targetFp, fiberToNodeId) {
|
|
3917
|
+
const deps = consumer.dependencies?.firstContext;
|
|
3918
|
+
if (!deps) return null;
|
|
3919
|
+
let dep = deps;
|
|
3920
|
+
while (dep) {
|
|
3921
|
+
const match = valuesMatch(target, targetFp, dep.memoizedValue);
|
|
3922
|
+
if (match) {
|
|
3923
|
+
const provider = findNearestProvider(consumer, dep.context);
|
|
3924
|
+
const step = {
|
|
3925
|
+
kind: "context",
|
|
3926
|
+
contextName: dep.context.displayName || "Context",
|
|
3927
|
+
providerNodeId: provider ? fiberToNodeId.get(provider) : void 0,
|
|
3928
|
+
confidence: match
|
|
3929
|
+
};
|
|
3930
|
+
const providerValue = provider?.memoizedProps?.value ?? dep.memoizedValue;
|
|
3931
|
+
return { step, providerValue };
|
|
3932
|
+
}
|
|
3933
|
+
dep = dep.next;
|
|
3934
|
+
}
|
|
3935
|
+
return null;
|
|
3936
|
+
}
|
|
3937
|
+
function findDerivationMatch(fiber, target, targetFp, componentName) {
|
|
3938
|
+
let hook = fiber.memoizedState;
|
|
3939
|
+
let index = 0;
|
|
3940
|
+
while (hook) {
|
|
3941
|
+
const ms = hook.memoizedState;
|
|
3942
|
+
if (Array.isArray(ms) && ms.length === 2 && Array.isArray(ms[1])) {
|
|
3943
|
+
const [computed, deps] = ms;
|
|
3944
|
+
const match = valuesMatch(target, targetFp, computed);
|
|
3945
|
+
if (match) {
|
|
3946
|
+
const hookType = typeof computed === "function" ? "useCallback" : "useMemo";
|
|
3947
|
+
return {
|
|
3948
|
+
kind: "derived",
|
|
3949
|
+
nodeId: "",
|
|
3950
|
+
componentName,
|
|
3951
|
+
hookIndex: index,
|
|
3952
|
+
hookType,
|
|
3953
|
+
depCount: deps.length,
|
|
3954
|
+
confidence: match
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
hook = hook.next;
|
|
3959
|
+
index++;
|
|
3960
|
+
}
|
|
3961
|
+
return null;
|
|
3962
|
+
}
|
|
3963
|
+
function findNearestProvider(consumer, contextObj) {
|
|
3964
|
+
let current = consumer.return;
|
|
3965
|
+
let hops = 0;
|
|
3966
|
+
while (current && hops < MAX_PROP_CHAIN_DEPTH) {
|
|
3967
|
+
if (current.tag === FIBER_TAG_CONTEXT_PROVIDER) {
|
|
3968
|
+
const providerType = current.type;
|
|
3969
|
+
if (providerType && providerType._context === contextObj) return current;
|
|
3970
|
+
}
|
|
3971
|
+
current = current.return;
|
|
3972
|
+
hops++;
|
|
3973
|
+
}
|
|
3974
|
+
return null;
|
|
3975
|
+
}
|
|
3976
|
+
function findStoreMatch(target, targetFp, deadline) {
|
|
3977
|
+
for (const [storeName, state] of getZustandSnapshot()) {
|
|
3978
|
+
if (now() > deadline) return null;
|
|
3979
|
+
const hit = findMatchingPathInObject(target, targetFp, state, [], 0, deadline);
|
|
3980
|
+
if (hit) {
|
|
3981
|
+
return {
|
|
3982
|
+
source: "zustand",
|
|
3983
|
+
storeName,
|
|
3984
|
+
keyPath: hit.path,
|
|
3985
|
+
confidence: hit.confidence,
|
|
3986
|
+
matchedValue: walkPath(state, hit.path)
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
const redux = getReduxSnapshot();
|
|
3991
|
+
if (redux) {
|
|
3992
|
+
if (now() > deadline) return null;
|
|
3993
|
+
const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, deadline);
|
|
3994
|
+
if (hit) {
|
|
3995
|
+
return {
|
|
3996
|
+
source: "redux",
|
|
3997
|
+
storeName: "redux",
|
|
3998
|
+
keyPath: hit.path,
|
|
3999
|
+
confidence: hit.confidence,
|
|
4000
|
+
matchedValue: walkPath(redux, hit.path)
|
|
4001
|
+
};
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
for (const [queryHash, entry] of getTanstackSnapshot()) {
|
|
4005
|
+
if (now() > deadline) return null;
|
|
4006
|
+
const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, deadline);
|
|
4007
|
+
if (hit) {
|
|
4008
|
+
return {
|
|
4009
|
+
source: "tanstack-query",
|
|
4010
|
+
storeName: queryHash,
|
|
4011
|
+
keyPath: hit.path,
|
|
4012
|
+
confidence: hit.confidence,
|
|
4013
|
+
matchedValue: walkPath(entry.data, hit.path)
|
|
4014
|
+
};
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
return null;
|
|
4018
|
+
}
|
|
4019
|
+
|
|
3662
4020
|
// src/frameworkDetect.ts
|
|
3663
4021
|
function detectWebFramework() {
|
|
3664
4022
|
if (typeof document === "undefined") return {};
|
|
@@ -3694,8 +4052,11 @@ function detectWebFramework() {
|
|
|
3694
4052
|
getNodeEffects,
|
|
3695
4053
|
getNodeHooks,
|
|
3696
4054
|
getNodeProps,
|
|
4055
|
+
getReduxSnapshot,
|
|
4056
|
+
getTanstackSnapshot,
|
|
3697
4057
|
getTimeline,
|
|
3698
4058
|
getWebSocketClient,
|
|
4059
|
+
getZustandSnapshot,
|
|
3699
4060
|
hasActiveTags,
|
|
3700
4061
|
inspectEffects,
|
|
3701
4062
|
inspectHooks,
|
|
@@ -3712,6 +4073,7 @@ function detectWebFramework() {
|
|
|
3712
4073
|
requestFullSnapshot,
|
|
3713
4074
|
requestTreeSnapshot,
|
|
3714
4075
|
resetNextjsDetection,
|
|
4076
|
+
resolveValueTrace,
|
|
3715
4077
|
serializeProps,
|
|
3716
4078
|
serializeValue,
|
|
3717
4079
|
tagFetchData,
|