@flotrace/runtime-core 2.2.2 → 2.2.4
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/LICENSE +21 -0
- package/README.md +33 -2
- package/dist/index.d.mts +3 -5
- package/dist/index.d.ts +3 -5
- package/dist/index.js +23 -23
- package/dist/index.mjs +23 -23
- package/package.json +9 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sameer Sitre
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -11,6 +11,33 @@ Platform-agnostic core for [FloTrace](https://flotrace.dev) — fiber walker, ho
|
|
|
11
11
|
|
|
12
12
|
`runtime-core` is published publicly so adapters can pin a compatible version and so users can audit the open-source half of FloTrace. It has zero runtime dependency on `window` / `document` / `XMLHttpRequest` — all platform-specific features live in the adapters.
|
|
13
13
|
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## About FloTrace Desktop
|
|
17
|
+
|
|
18
|
+
[**FloTrace Desktop**](https://flotrace.dev) is a free Electron app (macOS / Windows / Linux) that visualizes a running React app's component hierarchy in real time. It pairs with this runtime over a local WebSocket on port `3457` — the runtime sits inside your app and emits metadata; the desktop app renders the live tree, props, hooks, effects, and state. **Source code never leaves your machine.**
|
|
19
|
+
|
|
20
|
+
What you get when this runtime is paired with the desktop:
|
|
21
|
+
|
|
22
|
+
- **Live component tree** — React Flow graph, render-flash animation, frequency-based heatmap, breadcrumb navigation.
|
|
23
|
+
- **Per-node inspection** — props (with diff history), hooks (14 classified types + dep diffs), effects (willRun + dep diffs), component timeline.
|
|
24
|
+
- **State tracking** — Zustand (per-store), Redux (with change highlighting), Router, TanStack Query (with health warnings + wasted-refetch detection), Context.
|
|
25
|
+
- **Render cascade tracing** — trigger log, cascade tree, flame chart, cascade compare modal.
|
|
26
|
+
- **Prop drilling detection** — chain detection (≥3 levels deep), severity badges, heatmap overlay, refactor recommendations.
|
|
27
|
+
- **Network health** — fetch / XHR tracking, method badges, status dots, duplicate detection, API → store causal correlation.
|
|
28
|
+
- **Watch expressions** — pin values from 8 sources (Zustand / Redux / Router / Context / Props / Hooks / TanStack Query / API).
|
|
29
|
+
- **AI Code Review Dashboard** — 6-tab review (Re-renders, Memo, Drilling, Effects, Compiler, Network) with Lighthouse-style scores.
|
|
30
|
+
- **Copy-as-Prompt** — turn any panel into an AI-ready prompt for Cursor / Claude / ChatGPT in one click.
|
|
31
|
+
|
|
32
|
+
How it fits together:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
your React app ←→ @flotrace/runtime[-native] ←→ ws://localhost:3457 ←→ FloTrace Desktop
|
|
36
|
+
(this stack — open source, MIT) (closed-source commercial)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
[**Download FloTrace Desktop →**](https://flotrace.dev) · [Docs](https://flotrace.dev/docs) · [Security model](https://flotrace.dev/security)
|
|
40
|
+
|
|
14
41
|
## What's inside
|
|
15
42
|
|
|
16
43
|
| Module | Purpose |
|
|
@@ -38,8 +65,12 @@ The runtime is what lives inside your app. Open-source means you can read every
|
|
|
38
65
|
|
|
39
66
|
## Contributing
|
|
40
67
|
|
|
41
|
-
Issues and PRs welcome at [github.com/
|
|
68
|
+
Issues and PRs welcome at [github.com/sameersitre/runtime-core](https://github.com/sameersitre/runtime-core). The runtime packages target Hermes, V8 (Chromium), and JavaScriptCore — please test against all three when changing fiber-walker or serializer code.
|
|
42
69
|
|
|
43
70
|
## License
|
|
44
71
|
|
|
45
|
-
MIT.
|
|
72
|
+
MIT — see [LICENSE](./LICENSE).
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
> **Mirrored from the [flotrace-desktop](https://github.com/sameersitre/flotrace-desktop) monorepo.** This repo is read-only — every release is regenerated by the lockstep publisher in the desktop monorepo. Issues filed here are tracked, but PRs are best opened against the upstream monorepo where the canonical source lives.
|
package/dist/index.d.mts
CHANGED
|
@@ -708,15 +708,12 @@ interface ValueTrace {
|
|
|
708
708
|
steps: TraceStep[];
|
|
709
709
|
/** Wall-clock time the resolver completed. */
|
|
710
710
|
resolvedAtMs: number;
|
|
711
|
-
/** True when the 50ms budget tripped and the chain is partial. */
|
|
712
|
-
truncated?: boolean;
|
|
713
711
|
/**
|
|
714
712
|
* Optional error hint for friendly empty states.
|
|
715
713
|
* - `value-not-found`: target path doesn't exist on the current fiber.
|
|
716
714
|
* - `no-fiber`: nodeId no longer present in fiberRefMap (component unmounted).
|
|
717
|
-
* - `budget-exceeded`: bailed before finding origin.
|
|
718
715
|
*/
|
|
719
|
-
error?: 'value-not-found' | 'no-fiber'
|
|
716
|
+
error?: 'value-not-found' | 'no-fiber';
|
|
720
717
|
}
|
|
721
718
|
interface RuntimeValueTraceMessage {
|
|
722
719
|
type: 'runtime:valueTrace';
|
|
@@ -898,7 +895,8 @@ declare const DEFAULT_CONFIG: ResolvedFloTraceConfig;
|
|
|
898
895
|
* 2. Store match — scan Zustand / Redux / TanStack Query live snapshots
|
|
899
896
|
* 3. API match — feed matched reference into fetchOriginRegistry WeakMap
|
|
900
897
|
*
|
|
901
|
-
* On-demand only — never runs per-render.
|
|
898
|
+
* On-demand only — never runs per-render. Bounded by MAX_PROP_CHAIN_DEPTH and
|
|
899
|
+
* SCAN_DEPTH structural limits; no wall-clock budget so every trace runs to origin.
|
|
902
900
|
* Uses reference identity (`===`) before fingerprinting to keep common cases fast.
|
|
903
901
|
*
|
|
904
902
|
* See docs/PRD-VALUE-LINEAGE.md §6 and docs/IMPLEMENTATION-PLAN-VALUE-LINEAGE.md Phase 2.
|
package/dist/index.d.ts
CHANGED
|
@@ -708,15 +708,12 @@ interface ValueTrace {
|
|
|
708
708
|
steps: TraceStep[];
|
|
709
709
|
/** Wall-clock time the resolver completed. */
|
|
710
710
|
resolvedAtMs: number;
|
|
711
|
-
/** True when the 50ms budget tripped and the chain is partial. */
|
|
712
|
-
truncated?: boolean;
|
|
713
711
|
/**
|
|
714
712
|
* Optional error hint for friendly empty states.
|
|
715
713
|
* - `value-not-found`: target path doesn't exist on the current fiber.
|
|
716
714
|
* - `no-fiber`: nodeId no longer present in fiberRefMap (component unmounted).
|
|
717
|
-
* - `budget-exceeded`: bailed before finding origin.
|
|
718
715
|
*/
|
|
719
|
-
error?: 'value-not-found' | 'no-fiber'
|
|
716
|
+
error?: 'value-not-found' | 'no-fiber';
|
|
720
717
|
}
|
|
721
718
|
interface RuntimeValueTraceMessage {
|
|
722
719
|
type: 'runtime:valueTrace';
|
|
@@ -898,7 +895,8 @@ declare const DEFAULT_CONFIG: ResolvedFloTraceConfig;
|
|
|
898
895
|
* 2. Store match — scan Zustand / Redux / TanStack Query live snapshots
|
|
899
896
|
* 3. API match — feed matched reference into fetchOriginRegistry WeakMap
|
|
900
897
|
*
|
|
901
|
-
* On-demand only — never runs per-render.
|
|
898
|
+
* On-demand only — never runs per-render. Bounded by MAX_PROP_CHAIN_DEPTH and
|
|
899
|
+
* SCAN_DEPTH structural limits; no wall-clock budget so every trace runs to origin.
|
|
902
900
|
* Uses reference identity (`===`) before fingerprinting to keep common cases fast.
|
|
903
901
|
*
|
|
904
902
|
* See docs/PRD-VALUE-LINEAGE.md §6 and docs/IMPLEMENTATION-PLAN-VALUE-LINEAGE.md Phase 2.
|
package/dist/index.js
CHANGED
|
@@ -1847,20 +1847,23 @@ function extractRoute(url) {
|
|
|
1847
1847
|
var originalFetch = null;
|
|
1848
1848
|
var interceptorClient = null;
|
|
1849
1849
|
var isInstalled2 = false;
|
|
1850
|
+
var patchedFetchRef = null;
|
|
1850
1851
|
function installRscPayloadInterceptor(client2) {
|
|
1851
1852
|
if (isInstalled2 || typeof globalThis.fetch !== "function") return;
|
|
1852
1853
|
isInstalled2 = true;
|
|
1853
1854
|
interceptorClient = client2;
|
|
1854
1855
|
originalFetch = globalThis.fetch;
|
|
1855
|
-
|
|
1856
|
+
const capturedOriginalFetch = originalFetch;
|
|
1857
|
+
const capturedClient = client2;
|
|
1858
|
+
const patchedFetch = async function patchedFetch2(input, init) {
|
|
1856
1859
|
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1857
1860
|
const isRscRequest = RSC_URL_PATTERNS.some((p) => p.test(url));
|
|
1858
|
-
const response = await
|
|
1859
|
-
if (isRscRequest && interceptorClient
|
|
1861
|
+
const response = await capturedOriginalFetch.call(globalThis, input, init);
|
|
1862
|
+
if (isRscRequest && interceptorClient === capturedClient && capturedClient.connected) {
|
|
1860
1863
|
try {
|
|
1861
1864
|
const sizeHeader = response.headers.get("content-length");
|
|
1862
1865
|
const payloadSizeBytes = sizeHeader ? parseInt(sizeHeader, 10) : 0;
|
|
1863
|
-
|
|
1866
|
+
capturedClient.send({
|
|
1864
1867
|
type: "runtime:rscPayload",
|
|
1865
1868
|
route: extractRoute(url),
|
|
1866
1869
|
payloadSizeBytes: isNaN(payloadSizeBytes) ? 0 : payloadSizeBytes,
|
|
@@ -1872,11 +1875,16 @@ function installRscPayloadInterceptor(client2) {
|
|
|
1872
1875
|
}
|
|
1873
1876
|
return response;
|
|
1874
1877
|
};
|
|
1878
|
+
patchedFetchRef = patchedFetch;
|
|
1879
|
+
globalThis.fetch = patchedFetch;
|
|
1875
1880
|
}
|
|
1876
1881
|
function uninstallRscPayloadInterceptor() {
|
|
1877
1882
|
if (!isInstalled2 || !originalFetch) return;
|
|
1878
|
-
globalThis.fetch
|
|
1883
|
+
if (globalThis.fetch === patchedFetchRef) {
|
|
1884
|
+
globalThis.fetch = originalFetch;
|
|
1885
|
+
}
|
|
1879
1886
|
originalFetch = null;
|
|
1887
|
+
patchedFetchRef = null;
|
|
1880
1888
|
interceptorClient = null;
|
|
1881
1889
|
isInstalled2 = false;
|
|
1882
1890
|
}
|
|
@@ -3722,7 +3730,6 @@ function safeCall(fn, fallback) {
|
|
|
3722
3730
|
|
|
3723
3731
|
// src/valueTraceResolver.ts
|
|
3724
3732
|
var FIBER_TAG_CONTEXT_PROVIDER = 10;
|
|
3725
|
-
var BUDGET_MS = 100;
|
|
3726
3733
|
var SCAN_DEPTH = 3;
|
|
3727
3734
|
var MAX_PROP_CHAIN_DEPTH = 30;
|
|
3728
3735
|
function now() {
|
|
@@ -3769,8 +3776,7 @@ function findReferenceMatchAtTopLevel(target, container) {
|
|
|
3769
3776
|
}
|
|
3770
3777
|
return null;
|
|
3771
3778
|
}
|
|
3772
|
-
function findMatchingPathInObject(target, targetFp, container, currentPath, depth,
|
|
3773
|
-
if (now() > deadline) return null;
|
|
3779
|
+
function findMatchingPathInObject(target, targetFp, container, currentPath, depth, cache) {
|
|
3774
3780
|
if (depth > SCAN_DEPTH) return null;
|
|
3775
3781
|
if (container === null || typeof container !== "object") return null;
|
|
3776
3782
|
const selfMatch = valuesMatch(target, targetFp, container, cache);
|
|
@@ -3780,7 +3786,7 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
|
|
|
3780
3786
|
const child = container[i];
|
|
3781
3787
|
const directMatch = valuesMatch(target, targetFp, child, cache);
|
|
3782
3788
|
if (directMatch) return { path: [...currentPath, String(i)], confidence: directMatch };
|
|
3783
|
-
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, String(i)], depth + 1,
|
|
3789
|
+
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, String(i)], depth + 1, cache);
|
|
3784
3790
|
if (nested) return nested;
|
|
3785
3791
|
}
|
|
3786
3792
|
} else {
|
|
@@ -3788,7 +3794,7 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
|
|
|
3788
3794
|
const child = container[key];
|
|
3789
3795
|
const directMatch = valuesMatch(target, targetFp, child, cache);
|
|
3790
3796
|
if (directMatch) return { path: [...currentPath, key], confidence: directMatch };
|
|
3791
|
-
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, key], depth + 1,
|
|
3797
|
+
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, key], depth + 1, cache);
|
|
3792
3798
|
if (nested) return nested;
|
|
3793
3799
|
}
|
|
3794
3800
|
}
|
|
@@ -3855,8 +3861,6 @@ function resolveOriginViaTagOrKeyPath(matchedValue, stateRoot, keyPath) {
|
|
|
3855
3861
|
return findFetchOrigin(matchedValue, { ignoreTTL: true }) ?? findFetchOriginUpKeyPath(stateRoot, keyPath);
|
|
3856
3862
|
}
|
|
3857
3863
|
function resolveValueTrace(input) {
|
|
3858
|
-
const startedAt = now();
|
|
3859
|
-
const deadline = startedAt + BUDGET_MS;
|
|
3860
3864
|
const steps = [];
|
|
3861
3865
|
const base = {
|
|
3862
3866
|
rootNodeId: input.nodeId,
|
|
@@ -3913,7 +3917,6 @@ function resolveValueTrace(input) {
|
|
|
3913
3917
|
let current = fiber.return;
|
|
3914
3918
|
let hops = 0;
|
|
3915
3919
|
while (current && hops < MAX_PROP_CHAIN_DEPTH) {
|
|
3916
|
-
if (now() > deadline) return { ...base, steps, truncated: true, resolvedAtMs: now() };
|
|
3917
3920
|
if (current.tag !== FIBER_TAG_CONTEXT_PROVIDER) {
|
|
3918
3921
|
const props = current.memoizedProps;
|
|
3919
3922
|
if (props) {
|
|
@@ -3921,7 +3924,7 @@ function resolveValueTrace(input) {
|
|
|
3921
3924
|
let matchPath = refKey !== null ? [refKey] : null;
|
|
3922
3925
|
let matchConfidence = "exact";
|
|
3923
3926
|
if (matchPath === null) {
|
|
3924
|
-
const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0,
|
|
3927
|
+
const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0, fpCache);
|
|
3925
3928
|
if (match) {
|
|
3926
3929
|
matchPath = match.path;
|
|
3927
3930
|
matchConfidence = match.confidence;
|
|
@@ -3985,7 +3988,7 @@ function resolveValueTrace(input) {
|
|
|
3985
3988
|
const contextMatch = findContextMatch(fiber, rootValue, rootFp, fiberToNodeId, fpCache);
|
|
3986
3989
|
if (contextMatch) {
|
|
3987
3990
|
steps.push(contextMatch.step);
|
|
3988
|
-
const providerStoreMatch = findStoreMatch(contextMatch.providerValue, cachedFp(contextMatch.providerValue, fpCache),
|
|
3991
|
+
const providerStoreMatch = findStoreMatch(contextMatch.providerValue, cachedFp(contextMatch.providerValue, fpCache), fpCache);
|
|
3989
3992
|
if (providerStoreMatch) {
|
|
3990
3993
|
steps.push({
|
|
3991
3994
|
kind: "store",
|
|
@@ -4010,7 +4013,7 @@ function resolveValueTrace(input) {
|
|
|
4010
4013
|
}
|
|
4011
4014
|
return { ...base, steps, resolvedAtMs: now() };
|
|
4012
4015
|
}
|
|
4013
|
-
const storeMatch = findStoreMatch(rootValue, rootFp,
|
|
4016
|
+
const storeMatch = findStoreMatch(rootValue, rootFp, fpCache);
|
|
4014
4017
|
if (storeMatch) {
|
|
4015
4018
|
steps.push({
|
|
4016
4019
|
kind: "store",
|
|
@@ -4115,10 +4118,9 @@ function findNearestProvider(consumer, contextObj) {
|
|
|
4115
4118
|
}
|
|
4116
4119
|
return null;
|
|
4117
4120
|
}
|
|
4118
|
-
function findStoreMatch(target, targetFp,
|
|
4121
|
+
function findStoreMatch(target, targetFp, cache) {
|
|
4119
4122
|
for (const [storeName, state] of getZustandSnapshot()) {
|
|
4120
|
-
|
|
4121
|
-
const hit = findMatchingPathInObject(target, targetFp, state, [], 0, deadline, cache);
|
|
4123
|
+
const hit = findMatchingPathInObject(target, targetFp, state, [], 0, cache);
|
|
4122
4124
|
if (hit) {
|
|
4123
4125
|
return {
|
|
4124
4126
|
source: "zustand",
|
|
@@ -4132,8 +4134,7 @@ function findStoreMatch(target, targetFp, deadline, cache) {
|
|
|
4132
4134
|
}
|
|
4133
4135
|
const redux = getReduxSnapshot();
|
|
4134
4136
|
if (redux) {
|
|
4135
|
-
|
|
4136
|
-
const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, deadline, cache);
|
|
4137
|
+
const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, cache);
|
|
4137
4138
|
if (hit) {
|
|
4138
4139
|
return {
|
|
4139
4140
|
source: "redux",
|
|
@@ -4146,8 +4147,7 @@ function findStoreMatch(target, targetFp, deadline, cache) {
|
|
|
4146
4147
|
}
|
|
4147
4148
|
}
|
|
4148
4149
|
for (const [queryHash, entry] of getTanstackSnapshot()) {
|
|
4149
|
-
|
|
4150
|
-
const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, deadline, cache);
|
|
4150
|
+
const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, cache);
|
|
4151
4151
|
if (hit) {
|
|
4152
4152
|
return {
|
|
4153
4153
|
source: "tanstack-query",
|
package/dist/index.mjs
CHANGED
|
@@ -1765,20 +1765,23 @@ function extractRoute(url) {
|
|
|
1765
1765
|
var originalFetch = null;
|
|
1766
1766
|
var interceptorClient = null;
|
|
1767
1767
|
var isInstalled2 = false;
|
|
1768
|
+
var patchedFetchRef = null;
|
|
1768
1769
|
function installRscPayloadInterceptor(client2) {
|
|
1769
1770
|
if (isInstalled2 || typeof globalThis.fetch !== "function") return;
|
|
1770
1771
|
isInstalled2 = true;
|
|
1771
1772
|
interceptorClient = client2;
|
|
1772
1773
|
originalFetch = globalThis.fetch;
|
|
1773
|
-
|
|
1774
|
+
const capturedOriginalFetch = originalFetch;
|
|
1775
|
+
const capturedClient = client2;
|
|
1776
|
+
const patchedFetch = async function patchedFetch2(input, init) {
|
|
1774
1777
|
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
1775
1778
|
const isRscRequest = RSC_URL_PATTERNS.some((p) => p.test(url));
|
|
1776
|
-
const response = await
|
|
1777
|
-
if (isRscRequest && interceptorClient
|
|
1779
|
+
const response = await capturedOriginalFetch.call(globalThis, input, init);
|
|
1780
|
+
if (isRscRequest && interceptorClient === capturedClient && capturedClient.connected) {
|
|
1778
1781
|
try {
|
|
1779
1782
|
const sizeHeader = response.headers.get("content-length");
|
|
1780
1783
|
const payloadSizeBytes = sizeHeader ? parseInt(sizeHeader, 10) : 0;
|
|
1781
|
-
|
|
1784
|
+
capturedClient.send({
|
|
1782
1785
|
type: "runtime:rscPayload",
|
|
1783
1786
|
route: extractRoute(url),
|
|
1784
1787
|
payloadSizeBytes: isNaN(payloadSizeBytes) ? 0 : payloadSizeBytes,
|
|
@@ -1790,11 +1793,16 @@ function installRscPayloadInterceptor(client2) {
|
|
|
1790
1793
|
}
|
|
1791
1794
|
return response;
|
|
1792
1795
|
};
|
|
1796
|
+
patchedFetchRef = patchedFetch;
|
|
1797
|
+
globalThis.fetch = patchedFetch;
|
|
1793
1798
|
}
|
|
1794
1799
|
function uninstallRscPayloadInterceptor() {
|
|
1795
1800
|
if (!isInstalled2 || !originalFetch) return;
|
|
1796
|
-
globalThis.fetch
|
|
1801
|
+
if (globalThis.fetch === patchedFetchRef) {
|
|
1802
|
+
globalThis.fetch = originalFetch;
|
|
1803
|
+
}
|
|
1797
1804
|
originalFetch = null;
|
|
1805
|
+
patchedFetchRef = null;
|
|
1798
1806
|
interceptorClient = null;
|
|
1799
1807
|
isInstalled2 = false;
|
|
1800
1808
|
}
|
|
@@ -3640,7 +3648,6 @@ function safeCall(fn, fallback) {
|
|
|
3640
3648
|
|
|
3641
3649
|
// src/valueTraceResolver.ts
|
|
3642
3650
|
var FIBER_TAG_CONTEXT_PROVIDER = 10;
|
|
3643
|
-
var BUDGET_MS = 100;
|
|
3644
3651
|
var SCAN_DEPTH = 3;
|
|
3645
3652
|
var MAX_PROP_CHAIN_DEPTH = 30;
|
|
3646
3653
|
function now() {
|
|
@@ -3687,8 +3694,7 @@ function findReferenceMatchAtTopLevel(target, container) {
|
|
|
3687
3694
|
}
|
|
3688
3695
|
return null;
|
|
3689
3696
|
}
|
|
3690
|
-
function findMatchingPathInObject(target, targetFp, container, currentPath, depth,
|
|
3691
|
-
if (now() > deadline) return null;
|
|
3697
|
+
function findMatchingPathInObject(target, targetFp, container, currentPath, depth, cache) {
|
|
3692
3698
|
if (depth > SCAN_DEPTH) return null;
|
|
3693
3699
|
if (container === null || typeof container !== "object") return null;
|
|
3694
3700
|
const selfMatch = valuesMatch(target, targetFp, container, cache);
|
|
@@ -3698,7 +3704,7 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
|
|
|
3698
3704
|
const child = container[i];
|
|
3699
3705
|
const directMatch = valuesMatch(target, targetFp, child, cache);
|
|
3700
3706
|
if (directMatch) return { path: [...currentPath, String(i)], confidence: directMatch };
|
|
3701
|
-
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, String(i)], depth + 1,
|
|
3707
|
+
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, String(i)], depth + 1, cache);
|
|
3702
3708
|
if (nested) return nested;
|
|
3703
3709
|
}
|
|
3704
3710
|
} else {
|
|
@@ -3706,7 +3712,7 @@ function findMatchingPathInObject(target, targetFp, container, currentPath, dept
|
|
|
3706
3712
|
const child = container[key];
|
|
3707
3713
|
const directMatch = valuesMatch(target, targetFp, child, cache);
|
|
3708
3714
|
if (directMatch) return { path: [...currentPath, key], confidence: directMatch };
|
|
3709
|
-
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, key], depth + 1,
|
|
3715
|
+
const nested = findMatchingPathInObject(target, targetFp, child, [...currentPath, key], depth + 1, cache);
|
|
3710
3716
|
if (nested) return nested;
|
|
3711
3717
|
}
|
|
3712
3718
|
}
|
|
@@ -3773,8 +3779,6 @@ function resolveOriginViaTagOrKeyPath(matchedValue, stateRoot, keyPath) {
|
|
|
3773
3779
|
return findFetchOrigin(matchedValue, { ignoreTTL: true }) ?? findFetchOriginUpKeyPath(stateRoot, keyPath);
|
|
3774
3780
|
}
|
|
3775
3781
|
function resolveValueTrace(input) {
|
|
3776
|
-
const startedAt = now();
|
|
3777
|
-
const deadline = startedAt + BUDGET_MS;
|
|
3778
3782
|
const steps = [];
|
|
3779
3783
|
const base = {
|
|
3780
3784
|
rootNodeId: input.nodeId,
|
|
@@ -3831,7 +3835,6 @@ function resolveValueTrace(input) {
|
|
|
3831
3835
|
let current = fiber.return;
|
|
3832
3836
|
let hops = 0;
|
|
3833
3837
|
while (current && hops < MAX_PROP_CHAIN_DEPTH) {
|
|
3834
|
-
if (now() > deadline) return { ...base, steps, truncated: true, resolvedAtMs: now() };
|
|
3835
3838
|
if (current.tag !== FIBER_TAG_CONTEXT_PROVIDER) {
|
|
3836
3839
|
const props = current.memoizedProps;
|
|
3837
3840
|
if (props) {
|
|
@@ -3839,7 +3842,7 @@ function resolveValueTrace(input) {
|
|
|
3839
3842
|
let matchPath = refKey !== null ? [refKey] : null;
|
|
3840
3843
|
let matchConfidence = "exact";
|
|
3841
3844
|
if (matchPath === null) {
|
|
3842
|
-
const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0,
|
|
3845
|
+
const match = findMatchingPathInObject(rootValue, rootFp, props, [], 0, fpCache);
|
|
3843
3846
|
if (match) {
|
|
3844
3847
|
matchPath = match.path;
|
|
3845
3848
|
matchConfidence = match.confidence;
|
|
@@ -3903,7 +3906,7 @@ function resolveValueTrace(input) {
|
|
|
3903
3906
|
const contextMatch = findContextMatch(fiber, rootValue, rootFp, fiberToNodeId, fpCache);
|
|
3904
3907
|
if (contextMatch) {
|
|
3905
3908
|
steps.push(contextMatch.step);
|
|
3906
|
-
const providerStoreMatch = findStoreMatch(contextMatch.providerValue, cachedFp(contextMatch.providerValue, fpCache),
|
|
3909
|
+
const providerStoreMatch = findStoreMatch(contextMatch.providerValue, cachedFp(contextMatch.providerValue, fpCache), fpCache);
|
|
3907
3910
|
if (providerStoreMatch) {
|
|
3908
3911
|
steps.push({
|
|
3909
3912
|
kind: "store",
|
|
@@ -3928,7 +3931,7 @@ function resolveValueTrace(input) {
|
|
|
3928
3931
|
}
|
|
3929
3932
|
return { ...base, steps, resolvedAtMs: now() };
|
|
3930
3933
|
}
|
|
3931
|
-
const storeMatch = findStoreMatch(rootValue, rootFp,
|
|
3934
|
+
const storeMatch = findStoreMatch(rootValue, rootFp, fpCache);
|
|
3932
3935
|
if (storeMatch) {
|
|
3933
3936
|
steps.push({
|
|
3934
3937
|
kind: "store",
|
|
@@ -4033,10 +4036,9 @@ function findNearestProvider(consumer, contextObj) {
|
|
|
4033
4036
|
}
|
|
4034
4037
|
return null;
|
|
4035
4038
|
}
|
|
4036
|
-
function findStoreMatch(target, targetFp,
|
|
4039
|
+
function findStoreMatch(target, targetFp, cache) {
|
|
4037
4040
|
for (const [storeName, state] of getZustandSnapshot()) {
|
|
4038
|
-
|
|
4039
|
-
const hit = findMatchingPathInObject(target, targetFp, state, [], 0, deadline, cache);
|
|
4041
|
+
const hit = findMatchingPathInObject(target, targetFp, state, [], 0, cache);
|
|
4040
4042
|
if (hit) {
|
|
4041
4043
|
return {
|
|
4042
4044
|
source: "zustand",
|
|
@@ -4050,8 +4052,7 @@ function findStoreMatch(target, targetFp, deadline, cache) {
|
|
|
4050
4052
|
}
|
|
4051
4053
|
const redux = getReduxSnapshot();
|
|
4052
4054
|
if (redux) {
|
|
4053
|
-
|
|
4054
|
-
const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, deadline, cache);
|
|
4055
|
+
const hit = findMatchingPathInObject(target, targetFp, redux, [], 0, cache);
|
|
4055
4056
|
if (hit) {
|
|
4056
4057
|
return {
|
|
4057
4058
|
source: "redux",
|
|
@@ -4064,8 +4065,7 @@ function findStoreMatch(target, targetFp, deadline, cache) {
|
|
|
4064
4065
|
}
|
|
4065
4066
|
}
|
|
4066
4067
|
for (const [queryHash, entry] of getTanstackSnapshot()) {
|
|
4067
|
-
|
|
4068
|
-
const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, deadline, cache);
|
|
4068
|
+
const hit = findMatchingPathInObject(target, targetFp, entry.data, [], 0, cache);
|
|
4069
4069
|
if (hit) {
|
|
4070
4070
|
return {
|
|
4071
4071
|
source: "tanstack-query",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flotrace/runtime-core",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "Platform-agnostic core for FloTrace runtime — fiber walker, analyzers, trackers. Shared by @flotrace/runtime (web) and @flotrace/runtime-native (React Native).",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"README.md"
|
|
17
19
|
],
|
|
18
20
|
"scripts": {
|
|
19
21
|
"build": "tsup",
|
|
@@ -46,10 +48,13 @@
|
|
|
46
48
|
"flotrace"
|
|
47
49
|
],
|
|
48
50
|
"license": "MIT",
|
|
51
|
+
"homepage": "https://flotrace.dev",
|
|
49
52
|
"repository": {
|
|
50
53
|
"type": "git",
|
|
51
|
-
"url": "https://github.com/
|
|
52
|
-
|
|
54
|
+
"url": "https://github.com/sameersitre/runtime-core.git"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/sameersitre/runtime-core/issues"
|
|
53
58
|
},
|
|
54
59
|
"publishConfig": {
|
|
55
60
|
"access": "public"
|