@flotrace/runtime-core 2.0.0 → 2.2.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.d.mts +237 -4
- package/dist/index.d.ts +237 -4
- package/dist/index.js +413 -12
- package/dist/index.mjs +398 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -25,6 +35,7 @@ __export(index_exports, {
|
|
|
25
35
|
buildAncestorChain: () => buildAncestorChain,
|
|
26
36
|
clearFetchOriginTags: () => clearFetchOriginTags,
|
|
27
37
|
detectServerComponent: () => detectServerComponent,
|
|
38
|
+
detectWebFramework: () => detectWebFramework,
|
|
28
39
|
disposeWebSocketClient: () => disposeWebSocketClient,
|
|
29
40
|
findFetchOrigin: () => findFetchOrigin,
|
|
30
41
|
getChangedKeys: () => getChangedKeys,
|
|
@@ -35,8 +46,11 @@ __export(index_exports, {
|
|
|
35
46
|
getNodeEffects: () => getNodeEffects,
|
|
36
47
|
getNodeHooks: () => getNodeHooks,
|
|
37
48
|
getNodeProps: () => getNodeProps,
|
|
49
|
+
getReduxSnapshot: () => getReduxSnapshot,
|
|
50
|
+
getTanstackSnapshot: () => getTanstackSnapshot,
|
|
38
51
|
getTimeline: () => getTimeline,
|
|
39
52
|
getWebSocketClient: () => getWebSocketClient,
|
|
53
|
+
getZustandSnapshot: () => getZustandSnapshot,
|
|
40
54
|
hasActiveTags: () => hasActiveTags,
|
|
41
55
|
inspectEffects: () => inspectEffects,
|
|
42
56
|
inspectHooks: () => inspectHooks,
|
|
@@ -53,6 +67,7 @@ __export(index_exports, {
|
|
|
53
67
|
requestFullSnapshot: () => requestFullSnapshot,
|
|
54
68
|
requestTreeSnapshot: () => requestTreeSnapshot,
|
|
55
69
|
resetNextjsDetection: () => resetNextjsDetection,
|
|
70
|
+
resolveValueTrace: () => resolveValueTrace,
|
|
56
71
|
serializeProps: () => serializeProps,
|
|
57
72
|
serializeValue: () => serializeValue,
|
|
58
73
|
tagFetchData: () => tagFetchData,
|
|
@@ -243,6 +258,7 @@ function getChangedKeys(prev, next) {
|
|
|
243
258
|
}
|
|
244
259
|
|
|
245
260
|
// src/websocketClient.ts
|
|
261
|
+
var import_react = __toESM(require("react"));
|
|
246
262
|
var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
|
|
247
263
|
constructor(config = {}) {
|
|
248
264
|
this.ws = null;
|
|
@@ -290,7 +306,10 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
|
|
|
290
306
|
appUrl: this.config.getAppUrl?.(),
|
|
291
307
|
platform: this.config.platform,
|
|
292
308
|
appId: this.config.appId,
|
|
293
|
-
appVersion: this.config.appVersion
|
|
309
|
+
appVersion: this.config.appVersion,
|
|
310
|
+
frameworkName: this.config.frameworkName,
|
|
311
|
+
frameworkVersion: this.config.frameworkVersion,
|
|
312
|
+
reactNativeVersion: this.config.reactNativeVersion
|
|
294
313
|
});
|
|
295
314
|
this.flush();
|
|
296
315
|
};
|
|
@@ -474,12 +493,17 @@ var _FloTraceWebSocketClient = class _FloTraceWebSocketClient {
|
|
|
474
493
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
475
494
|
}
|
|
476
495
|
/**
|
|
477
|
-
* Get React version if available
|
|
496
|
+
* Get React version if available.
|
|
497
|
+
*
|
|
498
|
+
* Historical note: an earlier implementation read `globalThis.React?.version` —
|
|
499
|
+
* but React is an ES-module import in modern bundles (Vite/webpack/Next.js) and
|
|
500
|
+
* is never placed on `globalThis`, so the probe returned undefined for every
|
|
501
|
+
* typical bundled app. Reading `React.version` via a direct import is
|
|
502
|
+
* authoritative across web (both CJS and ESM bundles), React Native, and SSR.
|
|
478
503
|
*/
|
|
479
504
|
getReactVersion() {
|
|
480
505
|
try {
|
|
481
|
-
|
|
482
|
-
return React?.version;
|
|
506
|
+
return import_react.default.version;
|
|
483
507
|
} catch {
|
|
484
508
|
return void 0;
|
|
485
509
|
}
|
|
@@ -1609,8 +1633,8 @@ function runAnalysis(tree, fiberRefMap2) {
|
|
|
1609
1633
|
}
|
|
1610
1634
|
function schedulePropDrillingAnalysis(tree, fiberRefMap2, client2) {
|
|
1611
1635
|
if (analyzeTimer) clearTimeout(analyzeTimer);
|
|
1612
|
-
const
|
|
1613
|
-
const elapsed =
|
|
1636
|
+
const now2 = Date.now();
|
|
1637
|
+
const elapsed = now2 - lastAnalysisTime;
|
|
1614
1638
|
const delay = elapsed >= ANALYZE_INTERVAL_MS ? 0 : ANALYZE_INTERVAL_MS - elapsed;
|
|
1615
1639
|
analyzeTimer = setTimeout(() => {
|
|
1616
1640
|
analyzeTimer = null;
|
|
@@ -2637,7 +2661,7 @@ function executeSnapshot(root) {
|
|
|
2637
2661
|
}
|
|
2638
2662
|
previousFlatTree = currentFlatTree;
|
|
2639
2663
|
if (pendingLocalStateCorrelations.length > 0) {
|
|
2640
|
-
const
|
|
2664
|
+
const now2 = Date.now();
|
|
2641
2665
|
const toSend = pendingLocalStateCorrelations.splice(0);
|
|
2642
2666
|
for (const corr of toSend) {
|
|
2643
2667
|
try {
|
|
@@ -2646,7 +2670,7 @@ function executeSnapshot(root) {
|
|
|
2646
2670
|
requestId: corr.requestId,
|
|
2647
2671
|
componentName: corr.componentName,
|
|
2648
2672
|
hookIndex: corr.hookIndex,
|
|
2649
|
-
timestamp:
|
|
2673
|
+
timestamp: now2
|
|
2650
2674
|
});
|
|
2651
2675
|
} catch {
|
|
2652
2676
|
}
|
|
@@ -3091,6 +3115,7 @@ var activeUnsubscribers = [];
|
|
|
3091
3115
|
var isInstalled4 = false;
|
|
3092
3116
|
var debounceTimers = /* @__PURE__ */ new Map();
|
|
3093
3117
|
var DEBOUNCE_MS = 200;
|
|
3118
|
+
var trackedStores = /* @__PURE__ */ new Map();
|
|
3094
3119
|
function installZustandTracker(stores, client2) {
|
|
3095
3120
|
if (isInstalled4) {
|
|
3096
3121
|
console.warn("[FloTrace] Zustand tracker already installed, reinstalling");
|
|
@@ -3106,6 +3131,7 @@ function installZustandTracker(stores, client2) {
|
|
|
3106
3131
|
continue;
|
|
3107
3132
|
}
|
|
3108
3133
|
try {
|
|
3134
|
+
trackedStores.set(storeName, store);
|
|
3109
3135
|
const initialState = store.getState();
|
|
3110
3136
|
sendStoreUpdate(storeName, initialState, Object.keys(initialState), client2);
|
|
3111
3137
|
const unsubscribe = store.subscribe((newState, prevState) => {
|
|
@@ -3135,9 +3161,20 @@ function uninstallZustandTracker() {
|
|
|
3135
3161
|
}
|
|
3136
3162
|
}
|
|
3137
3163
|
activeUnsubscribers = [];
|
|
3164
|
+
trackedStores.clear();
|
|
3138
3165
|
isInstalled4 = false;
|
|
3139
3166
|
console.log("[FloTrace] Zustand tracker uninstalled");
|
|
3140
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
|
+
}
|
|
3141
3178
|
function scheduleStoreUpdate(storeName, prevState, newState, client2) {
|
|
3142
3179
|
let changedKeys;
|
|
3143
3180
|
try {
|
|
@@ -3176,6 +3213,7 @@ var isInstalled5 = false;
|
|
|
3176
3213
|
var debounceTimer = null;
|
|
3177
3214
|
var previousState = null;
|
|
3178
3215
|
var DEBOUNCE_MS2 = 200;
|
|
3216
|
+
var trackedStore = null;
|
|
3179
3217
|
function isReduxStore(obj) {
|
|
3180
3218
|
return typeof obj === "object" && obj !== null && typeof obj.getState === "function" && typeof obj.subscribe === "function" && typeof obj.dispatch === "function";
|
|
3181
3219
|
}
|
|
@@ -3187,6 +3225,7 @@ function installReduxTracker(store, client2) {
|
|
|
3187
3225
|
isInstalled5 = true;
|
|
3188
3226
|
console.log("[FloTrace] Installing Redux tracker");
|
|
3189
3227
|
try {
|
|
3228
|
+
trackedStore = store;
|
|
3190
3229
|
const initialState = store.getState();
|
|
3191
3230
|
previousState = initialState;
|
|
3192
3231
|
sendReduxUpdate(initialState, Object.keys(initialState), client2);
|
|
@@ -3218,9 +3257,18 @@ function uninstallReduxTracker() {
|
|
|
3218
3257
|
activeUnsubscribe = null;
|
|
3219
3258
|
}
|
|
3220
3259
|
previousState = null;
|
|
3260
|
+
trackedStore = null;
|
|
3221
3261
|
isInstalled5 = false;
|
|
3222
3262
|
console.log("[FloTrace] Redux tracker uninstalled");
|
|
3223
3263
|
}
|
|
3264
|
+
function getReduxSnapshot() {
|
|
3265
|
+
if (!trackedStore) return null;
|
|
3266
|
+
try {
|
|
3267
|
+
return trackedStore.getState();
|
|
3268
|
+
} catch {
|
|
3269
|
+
return null;
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3224
3272
|
function scheduleReduxUpdate(newState, client2) {
|
|
3225
3273
|
let changedKeys;
|
|
3226
3274
|
try {
|
|
@@ -3258,6 +3306,7 @@ var queryUnsubscribe = null;
|
|
|
3258
3306
|
var mutationUnsubscribe = null;
|
|
3259
3307
|
var debounceTimer2 = null;
|
|
3260
3308
|
var DEBOUNCE_MS3 = 300;
|
|
3309
|
+
var trackedClient = null;
|
|
3261
3310
|
var MAX_EVENTS_PER_QUERY = 50;
|
|
3262
3311
|
var queryTracking = /* @__PURE__ */ new Map();
|
|
3263
3312
|
var CORRELATION_WINDOW_MS = 500;
|
|
@@ -3280,6 +3329,7 @@ function installTanStackQueryTracker(queryClient, client2) {
|
|
|
3280
3329
|
isInstalled6 = true;
|
|
3281
3330
|
console.log("[FloTrace] Installing TanStack Query tracker");
|
|
3282
3331
|
try {
|
|
3332
|
+
trackedClient = queryClient;
|
|
3283
3333
|
const queryCache = queryClient.getQueryCache();
|
|
3284
3334
|
const mutationCache = queryClient.getMutationCache();
|
|
3285
3335
|
for (const query of queryCache.getAll()) {
|
|
@@ -3344,9 +3394,24 @@ function uninstallTanStackQueryTracker() {
|
|
|
3344
3394
|
clearTimeout(pending.timeoutId);
|
|
3345
3395
|
}
|
|
3346
3396
|
pendingCorrelations.clear();
|
|
3397
|
+
trackedClient = null;
|
|
3347
3398
|
isInstalled6 = false;
|
|
3348
3399
|
console.log("[FloTrace] TanStack Query tracker uninstalled");
|
|
3349
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
|
+
}
|
|
3350
3415
|
function computeDataHash(data) {
|
|
3351
3416
|
if (data === null || data === void 0) return "__null__";
|
|
3352
3417
|
try {
|
|
@@ -3409,11 +3474,11 @@ function updateQueryTracking(query, eventType) {
|
|
|
3409
3474
|
}
|
|
3410
3475
|
}
|
|
3411
3476
|
if (tracking.prevFetchStatus === "idle" && currentFetchStatus === "fetching") {
|
|
3412
|
-
const
|
|
3477
|
+
const now2 = Date.now();
|
|
3413
3478
|
for (const pending of pendingCorrelations.values()) {
|
|
3414
3479
|
if (pending.idleQueryHashes.has(query.queryHash)) {
|
|
3415
3480
|
pending.affectedQueries.set(query.queryHash, {
|
|
3416
|
-
fetchStartedAt:
|
|
3481
|
+
fetchStartedAt: now2,
|
|
3417
3482
|
queryKey: query.queryKey
|
|
3418
3483
|
});
|
|
3419
3484
|
}
|
|
@@ -3425,7 +3490,7 @@ function updateQueryTracking(query, eventType) {
|
|
|
3425
3490
|
}
|
|
3426
3491
|
function openCorrelationWindow(mutation, queryCache, mutationCache, client2) {
|
|
3427
3492
|
const correlationId = `corr-${++correlationCounter}`;
|
|
3428
|
-
const
|
|
3493
|
+
const now2 = Date.now();
|
|
3429
3494
|
const idleQueryHashes = /* @__PURE__ */ new Set();
|
|
3430
3495
|
for (const query of queryCache.getAll()) {
|
|
3431
3496
|
if (query.state.fetchStatus === "idle") {
|
|
@@ -3439,7 +3504,7 @@ function openCorrelationWindow(mutation, queryCache, mutationCache, client2) {
|
|
|
3439
3504
|
correlationId,
|
|
3440
3505
|
mutationId: mutation.mutationId,
|
|
3441
3506
|
mutationKey: mutation.options.mutationKey,
|
|
3442
|
-
completedAt:
|
|
3507
|
+
completedAt: now2,
|
|
3443
3508
|
idleQueryHashes,
|
|
3444
3509
|
affectedQueries: /* @__PURE__ */ new Map(),
|
|
3445
3510
|
timeoutId
|
|
@@ -3638,6 +3703,337 @@ function safeCall(fn, fallback) {
|
|
|
3638
3703
|
return fallback;
|
|
3639
3704
|
}
|
|
3640
3705
|
}
|
|
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
|
+
|
|
4020
|
+
// src/frameworkDetect.ts
|
|
4021
|
+
function detectWebFramework() {
|
|
4022
|
+
if (typeof document === "undefined") return {};
|
|
4023
|
+
const hasNextData = !!globalThis.__NEXT_DATA__ || !!document.querySelector("script#__NEXT_DATA__");
|
|
4024
|
+
if (!hasNextData) return { frameworkName: "plain-react" };
|
|
4025
|
+
let version;
|
|
4026
|
+
try {
|
|
4027
|
+
const req = globalThis.require;
|
|
4028
|
+
if (typeof req === "function") {
|
|
4029
|
+
const id = "next/package.json";
|
|
4030
|
+
const pkg = req(id);
|
|
4031
|
+
version = pkg?.version;
|
|
4032
|
+
}
|
|
4033
|
+
} catch {
|
|
4034
|
+
}
|
|
4035
|
+
return { frameworkName: "next", frameworkVersion: version };
|
|
4036
|
+
}
|
|
3641
4037
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3642
4038
|
0 && (module.exports = {
|
|
3643
4039
|
DEFAULT_CONFIG,
|
|
@@ -3645,6 +4041,7 @@ function safeCall(fn, fallback) {
|
|
|
3645
4041
|
buildAncestorChain,
|
|
3646
4042
|
clearFetchOriginTags,
|
|
3647
4043
|
detectServerComponent,
|
|
4044
|
+
detectWebFramework,
|
|
3648
4045
|
disposeWebSocketClient,
|
|
3649
4046
|
findFetchOrigin,
|
|
3650
4047
|
getChangedKeys,
|
|
@@ -3655,8 +4052,11 @@ function safeCall(fn, fallback) {
|
|
|
3655
4052
|
getNodeEffects,
|
|
3656
4053
|
getNodeHooks,
|
|
3657
4054
|
getNodeProps,
|
|
4055
|
+
getReduxSnapshot,
|
|
4056
|
+
getTanstackSnapshot,
|
|
3658
4057
|
getTimeline,
|
|
3659
4058
|
getWebSocketClient,
|
|
4059
|
+
getZustandSnapshot,
|
|
3660
4060
|
hasActiveTags,
|
|
3661
4061
|
inspectEffects,
|
|
3662
4062
|
inspectHooks,
|
|
@@ -3673,6 +4073,7 @@ function safeCall(fn, fallback) {
|
|
|
3673
4073
|
requestFullSnapshot,
|
|
3674
4074
|
requestTreeSnapshot,
|
|
3675
4075
|
resetNextjsDetection,
|
|
4076
|
+
resolveValueTrace,
|
|
3676
4077
|
serializeProps,
|
|
3677
4078
|
serializeValue,
|
|
3678
4079
|
tagFetchData,
|