@flotrace/runtime 0.1.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 +389 -0
- package/dist/index.d.mts +1364 -0
- package/dist/index.d.ts +1364 -0
- package/dist/index.js +4417 -0
- package/dist/index.mjs +4346 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# @flotrace/runtime
|
|
2
|
+
|
|
3
|
+
Runtime package for FloTrace — enables real-time React component tree visualization, render tracking, state management monitoring, and network health analysis in the FloTrace desktop app.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flotrace/runtime
|
|
9
|
+
# or
|
|
10
|
+
yarn add @flotrace/runtime
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @flotrace/runtime
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Peer dependencies:** React >= 16.9.0 (requires `<Profiler>` API)
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
Wrap your app with `<FloTraceProvider>`:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
23
|
+
import { createRoot } from 'react-dom/client';
|
|
24
|
+
import App from './App';
|
|
25
|
+
|
|
26
|
+
createRoot(document.getElementById('root')!).render(
|
|
27
|
+
<FloTraceProvider config={{ appName: 'My App' }}>
|
|
28
|
+
<App />
|
|
29
|
+
</FloTraceProvider>
|
|
30
|
+
);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then launch the FloTrace desktop app — your component tree will appear automatically.
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
All config options are optional. Pass them via the `config` prop:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<FloTraceProvider config={{ port: 3457, appName: 'My App' }}>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Option | Type | Default | Description |
|
|
44
|
+
|--------|------|---------|-------------|
|
|
45
|
+
| `port` | `number` | `3457` | WebSocket server port |
|
|
46
|
+
| `appName` | `string` | `'React App'` | App name displayed in FloTrace |
|
|
47
|
+
| `enabled` | `boolean` | `true` in dev | Enable/disable tracking |
|
|
48
|
+
| `autoReconnect` | `boolean` | `true` | Auto-reconnect on disconnect |
|
|
49
|
+
| `reconnectInterval` | `number` | `2000` | Reconnect interval (ms) |
|
|
50
|
+
| `trackAllRenders` | `boolean` | `true` | Track all component renders |
|
|
51
|
+
| `includeProps` | `boolean` | `true` | Include props in render events |
|
|
52
|
+
| `trackZustand` | `boolean` | `true` | Enable Zustand state tracking |
|
|
53
|
+
| `trackRedux` | `boolean` | `true` | Enable Redux state tracking |
|
|
54
|
+
| `trackRouter` | `boolean` | `true` | Enable URL navigation tracking |
|
|
55
|
+
| `trackContext` | `boolean` | `true` | Enable React Context tracking |
|
|
56
|
+
| `trackTanstackQuery` | `boolean` | `true` | Enable TanStack Query tracking |
|
|
57
|
+
|
|
58
|
+
## Framework Setup
|
|
59
|
+
|
|
60
|
+
### Vite + React
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// src/main.tsx
|
|
64
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
65
|
+
import { createRoot } from 'react-dom/client';
|
|
66
|
+
import App from './App';
|
|
67
|
+
|
|
68
|
+
createRoot(document.getElementById('root')!).render(
|
|
69
|
+
<FloTraceProvider config={{ appName: 'My Vite App' }}>
|
|
70
|
+
<App />
|
|
71
|
+
</FloTraceProvider>
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Next.js (App Router)
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// app/providers.tsx
|
|
79
|
+
'use client';
|
|
80
|
+
|
|
81
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
82
|
+
|
|
83
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
84
|
+
return (
|
|
85
|
+
<FloTraceProvider config={{ appName: 'My Next.js App' }}>
|
|
86
|
+
{children}
|
|
87
|
+
</FloTraceProvider>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// app/layout.tsx
|
|
92
|
+
import { Providers } from './providers';
|
|
93
|
+
|
|
94
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
95
|
+
return (
|
|
96
|
+
<html>
|
|
97
|
+
<body>
|
|
98
|
+
<Providers>{children}</Providers>
|
|
99
|
+
</body>
|
|
100
|
+
</html>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Next.js (Pages Router)
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
// pages/_app.tsx
|
|
109
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
110
|
+
import type { AppProps } from 'next/app';
|
|
111
|
+
|
|
112
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
113
|
+
return (
|
|
114
|
+
<FloTraceProvider config={{ appName: 'My Next.js App' }}>
|
|
115
|
+
<Component {...pageProps} />
|
|
116
|
+
</FloTraceProvider>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Create React App
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
// src/index.tsx
|
|
125
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
126
|
+
import { createRoot } from 'react-dom/client';
|
|
127
|
+
import App from './App';
|
|
128
|
+
|
|
129
|
+
createRoot(document.getElementById('root')!).render(
|
|
130
|
+
<FloTraceProvider config={{ appName: 'My CRA App' }}>
|
|
131
|
+
<App />
|
|
132
|
+
</FloTraceProvider>
|
|
133
|
+
);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## State Management Integration
|
|
137
|
+
|
|
138
|
+
### Zustand
|
|
139
|
+
|
|
140
|
+
Pass Zustand stores via the `stores` prop. Keys become store names in FloTrace:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
144
|
+
import { useBearStore } from './store/bearStore';
|
|
145
|
+
import { useUserStore } from './store/userStore';
|
|
146
|
+
|
|
147
|
+
<FloTraceProvider
|
|
148
|
+
stores={{ bearStore: useBearStore, userStore: useUserStore }}
|
|
149
|
+
config={{ appName: 'My App' }}
|
|
150
|
+
>
|
|
151
|
+
<App />
|
|
152
|
+
</FloTraceProvider>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Redux
|
|
156
|
+
|
|
157
|
+
Pass your Redux store via the `reduxStore` prop:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
161
|
+
import { store } from './store';
|
|
162
|
+
|
|
163
|
+
<FloTraceProvider
|
|
164
|
+
reduxStore={store}
|
|
165
|
+
config={{ appName: 'My App' }}
|
|
166
|
+
>
|
|
167
|
+
<App />
|
|
168
|
+
</FloTraceProvider>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### TanStack Query
|
|
172
|
+
|
|
173
|
+
Pass your TanStack Query client via the `queryClient` prop:
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
177
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
178
|
+
|
|
179
|
+
const queryClient = new QueryClient();
|
|
180
|
+
|
|
181
|
+
<QueryClientProvider client={queryClient}>
|
|
182
|
+
<FloTraceProvider
|
|
183
|
+
queryClient={queryClient}
|
|
184
|
+
config={{ appName: 'My App' }}
|
|
185
|
+
>
|
|
186
|
+
<App />
|
|
187
|
+
</FloTraceProvider>
|
|
188
|
+
</QueryClientProvider>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Combined (Zustand + Redux + TanStack Query + Router)
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import { FloTraceProvider } from '@flotrace/runtime';
|
|
195
|
+
import { store } from './reduxStore';
|
|
196
|
+
import { useBearStore } from './zustandStore';
|
|
197
|
+
import { queryClient } from './queryClient';
|
|
198
|
+
|
|
199
|
+
<FloTraceProvider
|
|
200
|
+
config={{ appName: 'My App' }}
|
|
201
|
+
stores={{ bearStore: useBearStore }}
|
|
202
|
+
reduxStore={store}
|
|
203
|
+
queryClient={queryClient}
|
|
204
|
+
>
|
|
205
|
+
<App />
|
|
206
|
+
</FloTraceProvider>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Router tracking is automatic — no configuration needed. It works with any SPA router (React Router, TanStack Router, Next.js, etc.) by patching the browser's History API.
|
|
210
|
+
|
|
211
|
+
## Advanced Usage
|
|
212
|
+
|
|
213
|
+
### `withFloTrace()` — HOC for targeted profiling
|
|
214
|
+
|
|
215
|
+
Wrap specific components to track their renders and props individually:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import { withFloTrace } from '@flotrace/runtime';
|
|
219
|
+
|
|
220
|
+
const ProfiledComponent = withFloTrace(MyComponent, 'MyComponent');
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### `useTrackProps()` — Track prop changes
|
|
224
|
+
|
|
225
|
+
Call at the top of a component to track its prop changes:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { useTrackProps } from '@flotrace/runtime';
|
|
229
|
+
|
|
230
|
+
function MyComponent(props: MyProps) {
|
|
231
|
+
useTrackProps('MyComponent', props);
|
|
232
|
+
// ... rest of component
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### `useFloTrace()` — Access runtime context
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { useFloTrace } from '@flotrace/runtime';
|
|
240
|
+
|
|
241
|
+
function DebugInfo() {
|
|
242
|
+
const floTrace = useFloTrace();
|
|
243
|
+
return <div>Connected: {floTrace?.connected ? 'Yes' : 'No'}</div>;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Production Safety
|
|
248
|
+
|
|
249
|
+
By default, FloTrace is **disabled in production** (`enabled` defaults to `process.env.NODE_ENV === 'development'`).
|
|
250
|
+
|
|
251
|
+
For explicit control, use a conditional import pattern:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// src/main.tsx
|
|
255
|
+
const FloTraceProvider = process.env.NODE_ENV === 'development'
|
|
256
|
+
? (await import('@flotrace/runtime')).FloTraceProvider
|
|
257
|
+
: ({ children }: { children: React.ReactNode }) => <>{children}</>;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Troubleshooting
|
|
261
|
+
|
|
262
|
+
### "Not connected"
|
|
263
|
+
|
|
264
|
+
1. Ensure the FloTrace desktop app is running
|
|
265
|
+
2. Check the port matches (default: 3457)
|
|
266
|
+
3. Verify `config.enabled` is `true`
|
|
267
|
+
4. Check browser console for `[FloTrace]` logs
|
|
268
|
+
|
|
269
|
+
### Zustand stores not appearing
|
|
270
|
+
|
|
271
|
+
- Stores must be passed explicitly via the `stores` prop
|
|
272
|
+
- Each value must be a Zustand hook (function with `.getState()` and `.subscribe()` methods)
|
|
273
|
+
- Check console for `[FloTrace] Skipping "..." — not a valid Zustand store` warnings
|
|
274
|
+
|
|
275
|
+
### Component tree is missing or incomplete
|
|
276
|
+
|
|
277
|
+
- The fiber tree walker needs React DevTools hook or DOM-based fiber access
|
|
278
|
+
- React DevTools browser extension improves reliability
|
|
279
|
+
- Tree depth is capped at 100 levels, children at 300 per node
|
|
280
|
+
- Reload the page if the tree appears empty
|
|
281
|
+
|
|
282
|
+
### WebSocket keeps reconnecting
|
|
283
|
+
|
|
284
|
+
- FloTrace uses exponential backoff (2s → 4s → 8s... up to 30s) with a 10-attempt budget
|
|
285
|
+
- After 10 failed attempts, reload the page or restart FloTrace to retry
|
|
286
|
+
- Check that no firewall or proxy is blocking `ws://localhost:3457`
|
|
287
|
+
|
|
288
|
+
## API Reference
|
|
289
|
+
|
|
290
|
+
### Components
|
|
291
|
+
|
|
292
|
+
| Export | Description |
|
|
293
|
+
|--------|-------------|
|
|
294
|
+
| `FloTraceProvider` | Main provider — wraps your app, manages WebSocket connection |
|
|
295
|
+
| `withFloTrace(Component, name?)` | HOC for targeted component profiling |
|
|
296
|
+
|
|
297
|
+
### Hooks
|
|
298
|
+
|
|
299
|
+
| Export | Description |
|
|
300
|
+
|--------|-------------|
|
|
301
|
+
| `useFloTrace()` | Access connection state and config |
|
|
302
|
+
| `useTrackProps(name, props)` | Track prop changes for a component |
|
|
303
|
+
|
|
304
|
+
### Types
|
|
305
|
+
|
|
306
|
+
| Export | Description |
|
|
307
|
+
|--------|-------------|
|
|
308
|
+
| `FloTraceConfig` | Configuration options interface |
|
|
309
|
+
| `FloTraceProviderProps` | Props for FloTraceProvider |
|
|
310
|
+
| `SerializedValue` | Serialized value type for safe WebSocket transmission |
|
|
311
|
+
| `LiveTreeNode` | Node in the live component tree |
|
|
312
|
+
| `ReduxStoreApi` | Minimal Redux store interface |
|
|
313
|
+
| `TanStackQueryClientApi` | Duck-typed TanStack Query client interface |
|
|
314
|
+
| `TrackingOptions` | Tracking options from extension |
|
|
315
|
+
| `DEFAULT_CONFIG` | Default configuration values |
|
|
316
|
+
| `DetailedRenderReason` | Detailed render reason with prop/state/context diffs |
|
|
317
|
+
| `HookType`, `HookInfo` | Hook type classification and inspection data |
|
|
318
|
+
| `EffectInfo` | Effect info with willRun and dep diffs |
|
|
319
|
+
| `TimelineEvent`, `TimelineEventType` | Component lifecycle events |
|
|
320
|
+
| `TanStackQueryInfo`, `TanStackMutationInfo` | Query and mutation tracking data |
|
|
321
|
+
| `NetworkRequestEntry` | Network request metadata (method, status, timing, correlation) |
|
|
322
|
+
| `Fiber`, `FiberHookState`, `FiberEffect` | React fiber type definitions |
|
|
323
|
+
|
|
324
|
+
### Advanced Exports
|
|
325
|
+
|
|
326
|
+
| Export | Description |
|
|
327
|
+
|--------|-------------|
|
|
328
|
+
| `getWebSocketClient(config?)` | Get singleton WebSocket client |
|
|
329
|
+
| `disposeWebSocketClient()` | Dispose the WebSocket client |
|
|
330
|
+
| `FloTraceWebSocketClient` | WebSocket client class |
|
|
331
|
+
| `installFiberTreeWalker()` | Manually install fiber tree walker |
|
|
332
|
+
| `uninstallFiberTreeWalker()` | Uninstall fiber tree walker |
|
|
333
|
+
| `requestTreeSnapshot()` | Request a tree snapshot (DOM fallback) |
|
|
334
|
+
| `getNodeHooks(nodeId)` | Inspect hooks for a specific component |
|
|
335
|
+
| `getNodeEffects(nodeId)` | Inspect effects for a specific component |
|
|
336
|
+
| `getDetailedRenderReason(nodeId)` | Get detailed render reason (prop/state/context diffs) |
|
|
337
|
+
| `getFiberRefMap()` | Get map of all tracked fiber refs |
|
|
338
|
+
| `inspectHooks(fiber)` | Classify and inspect hooks from fiber |
|
|
339
|
+
| `inspectEffects(fiber)` | Inspect effects from fiber updateQueue |
|
|
340
|
+
| `installZustandTracker()` / `uninstall...` | Zustand per-store subscription tracking |
|
|
341
|
+
| `installReduxTracker()` / `uninstall...` | Redux store subscription tracking |
|
|
342
|
+
| `installRouterTracker()` / `uninstall...` | History API patching for route tracking |
|
|
343
|
+
| `installTanStackQueryTracker()` / `uninstall...` | TanStack Query cache subscriber (duck-typed) |
|
|
344
|
+
| `installNetworkTracker()` / `uninstall...` | Fetch/XHR patching for network monitoring |
|
|
345
|
+
| `prewarmNetworkTracker()` | Pre-install patches to capture page-load requests |
|
|
346
|
+
| `installTimelineTracker()` / `uninstall...` | Component lifecycle event tracking |
|
|
347
|
+
| `recordTimelineEvent()` | Manually record a timeline event |
|
|
348
|
+
| `getTimeline(componentId)` | Get timeline events for a component |
|
|
349
|
+
| `serializeValue(value)` | Serialize a value for WebSocket |
|
|
350
|
+
| `serializeProps(props)` | Serialize props object |
|
|
351
|
+
| `isReduxStore(obj)` | Type guard for Redux store |
|
|
352
|
+
| `isTanStackQueryClient(obj)` | Type guard for TanStack Query client |
|
|
353
|
+
|
|
354
|
+
## Publishing
|
|
355
|
+
|
|
356
|
+
### Prerequisites
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
npm login
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Release Scripts
|
|
363
|
+
|
|
364
|
+
| Script | Version Change | When to Use |
|
|
365
|
+
|--------|---------------|-------------|
|
|
366
|
+
| `npm run release:patch` | `0.1.0` → `0.1.1` | Bug fixes, small tweaks |
|
|
367
|
+
| `npm run release:minor` | `0.1.0` → `0.2.0` | New features, non-breaking changes |
|
|
368
|
+
| `npm run release:major` | `0.1.0` → `1.0.0` | Breaking API changes |
|
|
369
|
+
|
|
370
|
+
Each release script automatically:
|
|
371
|
+
1. Bumps the version in `package.json`
|
|
372
|
+
2. Creates a git commit and tag (e.g. `v0.1.1`)
|
|
373
|
+
3. Cleans `dist/`, rebuilds, and typechecks (`prepublishOnly`)
|
|
374
|
+
4. Publishes to npm under `@flotrace` scope
|
|
375
|
+
|
|
376
|
+
### From the monorepo root
|
|
377
|
+
|
|
378
|
+
| Script | Action |
|
|
379
|
+
|--------|--------|
|
|
380
|
+
| `npm run runtime:release:patch` | Bump patch + build + publish (e.g. `0.1.0` → `0.1.1`) |
|
|
381
|
+
| `npm run runtime:release:minor` | Bump minor + build + publish (e.g. `0.1.0` → `0.2.0`) |
|
|
382
|
+
| `npm run runtime:release:major` | Bump major + build + publish (e.g. `0.1.0` → `1.0.0`) |
|
|
383
|
+
| `npm run runtime:publish` | Publish current version as-is (no version bump) |
|
|
384
|
+
|
|
385
|
+
> **Note:** `runtime:publish` publishes whatever version is currently in `package.json`. Use this for the first publish or to re-publish a fixed build without bumping the version. The `release:*` scripts auto-increment the version, create a git commit + tag, then publish.
|
|
386
|
+
|
|
387
|
+
## License
|
|
388
|
+
|
|
389
|
+
MIT
|