@flotrace/runtime 2.2.1 → 2.2.3
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 +114 -274
- package/dist/index.js +85 -5
- package/dist/index.mjs +85 -5
- package/package.json +10 -5
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
|
@@ -1,106 +1,95 @@
|
|
|
1
1
|
# @flotrace/runtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Stop guessing why your React app re-renders.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
FloTrace is a desktop app that shows you, live, what your React tree is doing — every render, every prop change, every state mutation, every cascade — without leaving your editor and without uploading your source code anywhere.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This package is the runtime that wires your app into the desktop. Drop it in once and you get:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```
|
|
9
|
+
- **A live component tree graph** — not a flat fiber list. See parent/child structure as it changes in real time.
|
|
10
|
+
- **Render reasons that actually answer "why?"** — props/state/context diffs, render frequency coloring, render-cascade tracing across files.
|
|
11
|
+
- **All your state in one panel** — Zustand, Redux, TanStack Query, Context, Router. No four browser extensions, no console.log.
|
|
12
|
+
- **Hooks + effects, classified and diffed** — 14 hook types, effect dep diffing, "did this effect re-run because X changed."
|
|
13
|
+
- **Network → state correlation** — every fetch/XHR mapped to the store update it caused.
|
|
14
|
+
- **Copy-as-Prompt** — turn any panel into an AI-ready prompt for Cursor/Claude/ChatGPT in one click.
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
Source code never leaves your machine. The runtime sends only metadata over `ws://localhost:3457` to the desktop app.
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
> **Using React Native?** Install [`@flotrace/runtime-native`](https://www.npmjs.com/package/@flotrace/runtime-native) instead. This package patches `fetch`/`XMLHttpRequest` in ways that crash the RN bridge.
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
[**Download the desktop app →**](https://flotrace.dev/download) · [Docs](https://flotrace.dev/docs) · [Compare to React DevTools](https://flotrace.dev/compare/react-devtools)
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
import { FloTraceProvider } from '@flotrace/runtime';
|
|
25
|
-
import { createRoot } from 'react-dom/client';
|
|
26
|
-
import App from './App';
|
|
22
|
+
---
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
<FloTraceProvider config={{ appName: 'My App' }}>
|
|
30
|
-
<App />
|
|
31
|
-
</FloTraceProvider>
|
|
32
|
-
);
|
|
33
|
-
```
|
|
24
|
+
## About FloTrace Desktop
|
|
34
25
|
|
|
35
|
-
|
|
26
|
+
[**FloTrace Desktop**](https://flotrace.dev) is a free Electron app (macOS / Windows / Linux) that visualizes your React app's component hierarchy in real time. This runtime package is the bridge: drop `<FloTraceProvider>` into your app and the desktop renders the live tree, with full inspection of props, hooks, effects, state, network calls, and render cascades.
|
|
36
27
|
|
|
37
|
-
|
|
28
|
+
When `@flotrace/runtime` (this package) is paired with the desktop, you get:
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
- **Live component tree** — React Flow graph, render-flash animation, frequency-based heatmap, breadcrumb bar, search-with-fitView.
|
|
31
|
+
- **Per-node inspection** — props (with diff history), hooks (14 classified types + dep diffs), effects (willRun + dep diffs), component timeline.
|
|
32
|
+
- **State tracking** — Zustand (per-store), Redux (with change highlighting), Router, TanStack Query (with health warnings + wasted-refetch detection), Context.
|
|
33
|
+
- **Render cascade tracing** — trigger log, cascade tree, flame chart, cascade compare modal.
|
|
34
|
+
- **Prop drilling detection** — chain detection (≥3 levels deep), severity badges, heatmap overlay, refactor recommendations.
|
|
35
|
+
- **Network health** — fetch / XHR tracking, method badges, status dots, duplicate detection, API → store causal correlation, pin-to-watch.
|
|
36
|
+
- **React 19 + Next.js** — Actions monitor, concurrent-update signals (useTransition / Suspense), compiler memo health, Next.js App Router detection, RSC payload interception.
|
|
37
|
+
- **Watch expressions** — pin values from 8 sources (Zustand / Redux / Router / Context / Props / Hooks / TanStack Query / API), max 20.
|
|
38
|
+
- **AI Code Review Dashboard** — 6-tab review (Re-renders, Memo, Drilling, Effects, Compiler, Network) with Lighthouse-style scores.
|
|
39
|
+
- **Copy-as-Prompt** — turn any panel into an AI-ready prompt for Cursor / Claude / ChatGPT in one click.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
How it fits together:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
your React app ←→ @flotrace/runtime ←→ ws://localhost:3457 ←→ FloTrace Desktop
|
|
45
|
+
(this package — open source, MIT) (closed-source commercial)
|
|
43
46
|
```
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
| `trackZustand` | `boolean` | `true` | Enable Zustand state tracking |
|
|
55
|
-
| `trackRedux` | `boolean` | `true` | Enable Redux state tracking |
|
|
56
|
-
| `trackRouter` | `boolean` | `true` | Enable URL navigation tracking |
|
|
57
|
-
| `trackContext` | `boolean` | `true` | Enable React Context tracking |
|
|
58
|
-
| `trackTanstackQuery` | `boolean` | `true` | Enable TanStack Query tracking |
|
|
59
|
-
|
|
60
|
-
## Framework Setup
|
|
61
|
-
|
|
62
|
-
### Vite + React
|
|
48
|
+
The desktop is free and binds to `127.0.0.1` only by default — your source code, props, and state never leave your machine.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 30-second setup
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install -D @flotrace/runtime
|
|
56
|
+
```
|
|
63
57
|
|
|
64
58
|
```tsx
|
|
65
|
-
// src/main.tsx
|
|
66
59
|
import { FloTraceProvider } from '@flotrace/runtime';
|
|
67
60
|
import { createRoot } from 'react-dom/client';
|
|
68
61
|
import App from './App';
|
|
69
62
|
|
|
70
63
|
createRoot(document.getElementById('root')!).render(
|
|
71
|
-
<FloTraceProvider config={{ appName: 'My
|
|
64
|
+
<FloTraceProvider config={{ appName: 'My App' }}>
|
|
72
65
|
<App />
|
|
73
66
|
</FloTraceProvider>
|
|
74
67
|
);
|
|
75
68
|
```
|
|
76
69
|
|
|
70
|
+
Launch the FloTrace desktop app. Reload your dev server. Done — your tree appears.
|
|
71
|
+
|
|
72
|
+
**Peer dependencies:** React >= 16.9.0 (uses `<Profiler>` API). Auto-disables in production via `process.env.NODE_ENV`.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Framework recipes
|
|
77
|
+
|
|
77
78
|
### Next.js (App Router)
|
|
78
79
|
|
|
79
80
|
```tsx
|
|
80
81
|
// app/providers.tsx
|
|
81
82
|
'use client';
|
|
82
|
-
|
|
83
83
|
import { FloTraceProvider } from '@flotrace/runtime';
|
|
84
84
|
|
|
85
85
|
export function Providers({ children }: { children: React.ReactNode }) {
|
|
86
|
-
return
|
|
87
|
-
<FloTraceProvider config={{ appName: 'My Next.js App' }}>
|
|
88
|
-
{children}
|
|
89
|
-
</FloTraceProvider>
|
|
90
|
-
);
|
|
86
|
+
return <FloTraceProvider config={{ appName: 'My Next.js App' }}>{children}</FloTraceProvider>;
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
// app/layout.tsx
|
|
94
90
|
import { Providers } from './providers';
|
|
95
|
-
|
|
96
91
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
97
|
-
return
|
|
98
|
-
<html>
|
|
99
|
-
<body>
|
|
100
|
-
<Providers>{children}</Providers>
|
|
101
|
-
</body>
|
|
102
|
-
</html>
|
|
103
|
-
);
|
|
92
|
+
return <html><body><Providers>{children}</Providers></body></html>;
|
|
104
93
|
}
|
|
105
94
|
```
|
|
106
95
|
|
|
@@ -120,272 +109,123 @@ export default function App({ Component, pageProps }: AppProps) {
|
|
|
120
109
|
}
|
|
121
110
|
```
|
|
122
111
|
|
|
123
|
-
### Create React App
|
|
112
|
+
### Vite / Create React App
|
|
124
113
|
|
|
125
|
-
|
|
126
|
-
// src/index.tsx
|
|
127
|
-
import { FloTraceProvider } from '@flotrace/runtime';
|
|
128
|
-
import { createRoot } from 'react-dom/client';
|
|
129
|
-
import App from './App';
|
|
114
|
+
Same as the 30-second setup above — wrap `<App />` at the root.
|
|
130
115
|
|
|
131
|
-
|
|
132
|
-
<FloTraceProvider config={{ appName: 'My CRA App' }}>
|
|
133
|
-
<App />
|
|
134
|
-
</FloTraceProvider>
|
|
135
|
-
);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## State Management Integration
|
|
116
|
+
---
|
|
139
117
|
|
|
140
|
-
|
|
118
|
+
## Wire up your state stores
|
|
141
119
|
|
|
142
|
-
Pass
|
|
120
|
+
State tracking is the killer feature. Pass your stores to the provider and they show up in the desktop's State panel with live diffs:
|
|
143
121
|
|
|
144
122
|
```tsx
|
|
145
123
|
import { FloTraceProvider } from '@flotrace/runtime';
|
|
146
|
-
import { useBearStore } from './
|
|
147
|
-
import {
|
|
124
|
+
import { useBearStore } from './zustandStore';
|
|
125
|
+
import { store as reduxStore } from './reduxStore';
|
|
126
|
+
import { queryClient } from './queryClient';
|
|
148
127
|
|
|
149
128
|
<FloTraceProvider
|
|
150
|
-
stores={{ bearStore: useBearStore, userStore: useUserStore }}
|
|
151
129
|
config={{ appName: 'My App' }}
|
|
130
|
+
stores={{ bearStore: useBearStore }} // Zustand — keys become store names
|
|
131
|
+
reduxStore={reduxStore} // Redux — pass your store directly
|
|
132
|
+
queryClient={queryClient} // TanStack Query — pass your client
|
|
152
133
|
>
|
|
153
134
|
<App />
|
|
154
135
|
</FloTraceProvider>
|
|
155
136
|
```
|
|
156
137
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Pass your Redux store via the `reduxStore` prop:
|
|
138
|
+
Router tracking is automatic — it patches the History API and works with React Router, TanStack Router, Next.js, etc.
|
|
160
139
|
|
|
161
|
-
|
|
162
|
-
import { FloTraceProvider } from '@flotrace/runtime';
|
|
163
|
-
import { store } from './store';
|
|
140
|
+
---
|
|
164
141
|
|
|
165
|
-
|
|
166
|
-
reduxStore={store}
|
|
167
|
-
config={{ appName: 'My App' }}
|
|
168
|
-
>
|
|
169
|
-
<App />
|
|
170
|
-
</FloTraceProvider>
|
|
171
|
-
```
|
|
142
|
+
## Configuration
|
|
172
143
|
|
|
173
|
-
|
|
144
|
+
All options optional. Sensible defaults for development.
|
|
174
145
|
|
|
175
|
-
|
|
146
|
+
| Option | Type | Default | Description |
|
|
147
|
+
|--------|------|---------|-------------|
|
|
148
|
+
| `appName` | `string` | `'React App'` | Shown in the FloTrace connection pill |
|
|
149
|
+
| `port` | `number` | `3457` | WebSocket server port |
|
|
150
|
+
| `enabled` | `boolean` | dev only | `false` to hard-disable |
|
|
151
|
+
| `autoReconnect` | `boolean` | `true` | Reconnect on disconnect |
|
|
152
|
+
| `reconnectInterval` | `number` | `2000` | Base reconnect delay (ms, exponential backoff) |
|
|
153
|
+
| `trackAllRenders` | `boolean` | `true` | Track every commit via `<Profiler>` |
|
|
154
|
+
| `includeProps` | `boolean` | `true` | Include prop values in render events |
|
|
155
|
+
| `trackZustand` / `trackRedux` / `trackRouter` / `trackContext` / `trackTanstackQuery` | `boolean` | `true` | Per-tracker toggles |
|
|
176
156
|
|
|
177
|
-
|
|
178
|
-
import { FloTraceProvider } from '@flotrace/runtime';
|
|
179
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
157
|
+
---
|
|
180
158
|
|
|
181
|
-
|
|
159
|
+
## Production safety
|
|
182
160
|
|
|
183
|
-
|
|
184
|
-
<FloTraceProvider
|
|
185
|
-
queryClient={queryClient}
|
|
186
|
-
config={{ appName: 'My App' }}
|
|
187
|
-
>
|
|
188
|
-
<App />
|
|
189
|
-
</FloTraceProvider>
|
|
190
|
-
</QueryClientProvider>
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Combined (Zustand + Redux + TanStack Query + Router)
|
|
161
|
+
FloTrace is **disabled in production** by default (`enabled` defaults to `process.env.NODE_ENV === 'development'`). For belt-and-braces tree-shaking, dynamic-import in dev only:
|
|
194
162
|
|
|
195
163
|
```tsx
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
import { queryClient } from './queryClient';
|
|
200
|
-
|
|
201
|
-
<FloTraceProvider
|
|
202
|
-
config={{ appName: 'My App' }}
|
|
203
|
-
stores={{ bearStore: useBearStore }}
|
|
204
|
-
reduxStore={store}
|
|
205
|
-
queryClient={queryClient}
|
|
206
|
-
>
|
|
207
|
-
<App />
|
|
208
|
-
</FloTraceProvider>
|
|
164
|
+
const FloTraceProvider = process.env.NODE_ENV === 'development'
|
|
165
|
+
? (await import('@flotrace/runtime')).FloTraceProvider
|
|
166
|
+
: ({ children }: { children: React.ReactNode }) => <>{children}</>;
|
|
209
167
|
```
|
|
210
168
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
## Advanced Usage
|
|
169
|
+
---
|
|
214
170
|
|
|
215
|
-
|
|
171
|
+
## Targeted profiling
|
|
216
172
|
|
|
217
|
-
|
|
173
|
+
For one-off component tracking without the full provider:
|
|
218
174
|
|
|
219
175
|
```tsx
|
|
220
|
-
import { withFloTrace } from '@flotrace/runtime';
|
|
221
|
-
|
|
222
|
-
const ProfiledComponent = withFloTrace(MyComponent, 'MyComponent');
|
|
223
|
-
```
|
|
176
|
+
import { withFloTrace, useTrackProps } from '@flotrace/runtime';
|
|
224
177
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Call at the top of a component to track its prop changes:
|
|
228
|
-
|
|
229
|
-
```tsx
|
|
230
|
-
import { useTrackProps } from '@flotrace/runtime';
|
|
178
|
+
const Profiled = withFloTrace(MyComponent, 'MyComponent');
|
|
231
179
|
|
|
232
180
|
function MyComponent(props: MyProps) {
|
|
233
181
|
useTrackProps('MyComponent', props);
|
|
234
|
-
// ...
|
|
182
|
+
// ...
|
|
235
183
|
}
|
|
236
184
|
```
|
|
237
185
|
|
|
238
|
-
|
|
186
|
+
---
|
|
239
187
|
|
|
240
|
-
|
|
241
|
-
import { useFloTrace } from '@flotrace/runtime';
|
|
242
|
-
|
|
243
|
-
function DebugInfo() {
|
|
244
|
-
const floTrace = useFloTrace();
|
|
245
|
-
return <div>Connected: {floTrace?.connected ? 'Yes' : 'No'}</div>;
|
|
246
|
-
}
|
|
247
|
-
```
|
|
188
|
+
## Privacy & security
|
|
248
189
|
|
|
249
|
-
|
|
190
|
+
- Source code never leaves your machine. The runtime sends only metadata (component names, prop types, render counts) over `ws://localhost:3457`.
|
|
191
|
+
- Desktop app binds to `127.0.0.1` only. LAN connections (physical devices) require an opt-in auth token.
|
|
192
|
+
- The desktop is closed-source commercial; this runtime package is **MIT-licensed** and open at [github.com/sameersitre/runtime](https://github.com/sameersitre/runtime).
|
|
250
193
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
For explicit control, use a conditional import pattern:
|
|
254
|
-
|
|
255
|
-
```tsx
|
|
256
|
-
// src/main.tsx
|
|
257
|
-
const FloTraceProvider = process.env.NODE_ENV === 'development'
|
|
258
|
-
? (await import('@flotrace/runtime')).FloTraceProvider
|
|
259
|
-
: ({ children }: { children: React.ReactNode }) => <>{children}</>;
|
|
260
|
-
```
|
|
194
|
+
---
|
|
261
195
|
|
|
262
196
|
## Troubleshooting
|
|
263
197
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
1. Ensure the FloTrace desktop app is running
|
|
267
|
-
2. Check the port matches (default: 3457)
|
|
268
|
-
3. Verify `config.enabled` is `true`
|
|
269
|
-
4. Check browser console for `[FloTrace]` logs
|
|
198
|
+
**"Not connected"** — Is the desktop app running? Is `enabled: true`? Check browser console for `[FloTrace]` logs.
|
|
270
199
|
|
|
271
|
-
|
|
200
|
+
**Zustand stores missing** — Stores must be passed via the `stores` prop, and each value must be a Zustand hook (has `.getState()` and `.subscribe()`).
|
|
272
201
|
|
|
273
|
-
-
|
|
274
|
-
- Each value must be a Zustand hook (function with `.getState()` and `.subscribe()` methods)
|
|
275
|
-
- Check console for `[FloTrace] Skipping "..." — not a valid Zustand store` warnings
|
|
202
|
+
**Tree empty or partial** — Reload the page. The walker uses React DevTools hook or DOM-based fiber access; depth is capped at 100, children at 300/node.
|
|
276
203
|
|
|
277
|
-
|
|
204
|
+
**WebSocket reconnecting** — Exponential backoff (2s → 30s, 10 attempts). After that, reload the page or restart the desktop.
|
|
278
205
|
|
|
279
|
-
|
|
280
|
-
- React DevTools browser extension improves reliability
|
|
281
|
-
- Tree depth is capped at 100 levels, children at 300 per node
|
|
282
|
-
- Reload the page if the tree appears empty
|
|
206
|
+
---
|
|
283
207
|
|
|
284
|
-
|
|
208
|
+
## API reference
|
|
285
209
|
|
|
286
|
-
|
|
287
|
-
- After 10 failed attempts, reload the page or restart FloTrace to retry
|
|
288
|
-
- Check that no firewall or proxy is blocking `ws://localhost:3457`
|
|
289
|
-
|
|
290
|
-
## API Reference
|
|
291
|
-
|
|
292
|
-
### Components
|
|
210
|
+
See [flotrace.dev/docs/runtime](https://flotrace.dev/docs/runtime) for the full export surface (40+ hooks, trackers, and types). Quick index:
|
|
293
211
|
|
|
294
212
|
| Export | Description |
|
|
295
|
-
|
|
296
|
-
| `FloTraceProvider` | Main provider
|
|
297
|
-
| `withFloTrace(Component, name?)` | HOC for targeted
|
|
213
|
+
|---|---|
|
|
214
|
+
| `FloTraceProvider` | Main provider component |
|
|
215
|
+
| `withFloTrace(Component, name?)` | HOC for targeted profiling |
|
|
216
|
+
| `useFloTrace()` | Connection state + config |
|
|
217
|
+
| `useTrackProps(name, props)` | Track prop changes |
|
|
218
|
+
| `installXxxTracker()` / `uninstallXxxTracker()` | Manual tracker control (Zustand, Redux, Router, TanStack Query, Network, Timeline) |
|
|
219
|
+
| `getNodeHooks(nodeId)` / `getNodeEffects(nodeId)` / `getDetailedRenderReason(nodeId)` | Inspector accessors |
|
|
298
220
|
|
|
299
|
-
|
|
221
|
+
Type exports: `FloTraceConfig`, `LiveTreeNode`, `SerializedValue`, `HookInfo`, `EffectInfo`, `TimelineEvent`, `NetworkRequestEntry`, `Fiber`, etc.
|
|
300
222
|
|
|
301
|
-
|
|
302
|
-
|--------|-------------|
|
|
303
|
-
| `useFloTrace()` | Access connection state and config |
|
|
304
|
-
| `useTrackProps(name, props)` | Track prop changes for a component |
|
|
223
|
+
---
|
|
305
224
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
| Export | Description |
|
|
309
|
-
|--------|-------------|
|
|
310
|
-
| `FloTraceConfig` | Configuration options interface |
|
|
311
|
-
| `FloTraceProviderProps` | Props for FloTraceProvider |
|
|
312
|
-
| `SerializedValue` | Serialized value type for safe WebSocket transmission |
|
|
313
|
-
| `LiveTreeNode` | Node in the live component tree |
|
|
314
|
-
| `ReduxStoreApi` | Minimal Redux store interface |
|
|
315
|
-
| `TanStackQueryClientApi` | Duck-typed TanStack Query client interface |
|
|
316
|
-
| `TrackingOptions` | Tracking options from extension |
|
|
317
|
-
| `DEFAULT_CONFIG` | Default configuration values |
|
|
318
|
-
| `DetailedRenderReason` | Detailed render reason with prop/state/context diffs |
|
|
319
|
-
| `HookType`, `HookInfo` | Hook type classification and inspection data |
|
|
320
|
-
| `EffectInfo` | Effect info with willRun and dep diffs |
|
|
321
|
-
| `TimelineEvent`, `TimelineEventType` | Component lifecycle events |
|
|
322
|
-
| `TanStackQueryInfo`, `TanStackMutationInfo` | Query and mutation tracking data |
|
|
323
|
-
| `NetworkRequestEntry` | Network request metadata (method, status, timing, correlation) |
|
|
324
|
-
| `Fiber`, `FiberHookState`, `FiberEffect` | React fiber type definitions |
|
|
325
|
-
|
|
326
|
-
### Advanced Exports
|
|
327
|
-
|
|
328
|
-
| Export | Description |
|
|
329
|
-
|--------|-------------|
|
|
330
|
-
| `getWebSocketClient(config?)` | Get singleton WebSocket client |
|
|
331
|
-
| `disposeWebSocketClient()` | Dispose the WebSocket client |
|
|
332
|
-
| `FloTraceWebSocketClient` | WebSocket client class |
|
|
333
|
-
| `installFiberTreeWalker()` | Manually install fiber tree walker |
|
|
334
|
-
| `uninstallFiberTreeWalker()` | Uninstall fiber tree walker |
|
|
335
|
-
| `requestTreeSnapshot()` | Request a tree snapshot (DOM fallback) |
|
|
336
|
-
| `getNodeHooks(nodeId)` | Inspect hooks for a specific component |
|
|
337
|
-
| `getNodeEffects(nodeId)` | Inspect effects for a specific component |
|
|
338
|
-
| `getDetailedRenderReason(nodeId)` | Get detailed render reason (prop/state/context diffs) |
|
|
339
|
-
| `getFiberRefMap()` | Get map of all tracked fiber refs |
|
|
340
|
-
| `inspectHooks(fiber)` | Classify and inspect hooks from fiber |
|
|
341
|
-
| `inspectEffects(fiber)` | Inspect effects from fiber updateQueue |
|
|
342
|
-
| `installZustandTracker()` / `uninstall...` | Zustand per-store subscription tracking |
|
|
343
|
-
| `installReduxTracker()` / `uninstall...` | Redux store subscription tracking |
|
|
344
|
-
| `installRouterTracker()` / `uninstall...` | History API patching for route tracking |
|
|
345
|
-
| `installTanStackQueryTracker()` / `uninstall...` | TanStack Query cache subscriber (duck-typed) |
|
|
346
|
-
| `installNetworkTracker()` / `uninstall...` | Fetch/XHR patching for network monitoring |
|
|
347
|
-
| `prewarmNetworkTracker()` | Pre-install patches to capture page-load requests |
|
|
348
|
-
| `installTimelineTracker()` / `uninstall...` | Component lifecycle event tracking |
|
|
349
|
-
| `recordTimelineEvent()` | Manually record a timeline event |
|
|
350
|
-
| `getTimeline(componentId)` | Get timeline events for a component |
|
|
351
|
-
| `serializeValue(value)` | Serialize a value for WebSocket |
|
|
352
|
-
| `serializeProps(props)` | Serialize props object |
|
|
353
|
-
| `isReduxStore(obj)` | Type guard for Redux store |
|
|
354
|
-
| `isTanStackQueryClient(obj)` | Type guard for TanStack Query client |
|
|
355
|
-
|
|
356
|
-
## Publishing
|
|
357
|
-
|
|
358
|
-
### Prerequisites
|
|
359
|
-
|
|
360
|
-
```bash
|
|
361
|
-
npm login
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### Release Scripts
|
|
365
|
-
|
|
366
|
-
| Script | Version Change | When to Use |
|
|
367
|
-
|--------|---------------|-------------|
|
|
368
|
-
| `npm run release:patch` | `0.1.0` → `0.1.1` | Bug fixes, small tweaks |
|
|
369
|
-
| `npm run release:minor` | `0.1.0` → `0.2.0` | New features, non-breaking changes |
|
|
370
|
-
| `npm run release:major` | `0.1.0` → `1.0.0` | Breaking API changes |
|
|
371
|
-
|
|
372
|
-
Each release script automatically:
|
|
373
|
-
1. Bumps the version in `package.json`
|
|
374
|
-
2. Creates a git commit and tag (e.g. `v0.1.1`)
|
|
375
|
-
3. Cleans `dist/`, rebuilds, and typechecks (`prepublishOnly`)
|
|
376
|
-
4. Publishes to npm under `@flotrace` scope
|
|
377
|
-
|
|
378
|
-
### From the monorepo root
|
|
379
|
-
|
|
380
|
-
| Script | Action |
|
|
381
|
-
|--------|--------|
|
|
382
|
-
| `npm run runtime:release:patch` | Bump patch + build + publish (e.g. `0.1.0` → `0.1.1`) |
|
|
383
|
-
| `npm run runtime:release:minor` | Bump minor + build + publish (e.g. `0.1.0` → `0.2.0`) |
|
|
384
|
-
| `npm run runtime:release:major` | Bump major + build + publish (e.g. `0.1.0` → `1.0.0`) |
|
|
385
|
-
| `npm run runtime:publish` | Publish current version as-is (no version bump) |
|
|
225
|
+
## License
|
|
386
226
|
|
|
387
|
-
|
|
227
|
+
MIT — see [LICENSE](./LICENSE). Issues and PRs welcome at [github.com/sameersitre/runtime](https://github.com/sameersitre/runtime).
|
|
388
228
|
|
|
389
|
-
|
|
229
|
+
---
|
|
390
230
|
|
|
391
|
-
|
|
231
|
+
> **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.js
CHANGED
|
@@ -47,6 +47,74 @@ __reExport(index_exports, require("@flotrace/runtime-core"), module.exports);
|
|
|
47
47
|
var import_react = __toESM(require("react"));
|
|
48
48
|
var import_runtime_core3 = require("@flotrace/runtime-core");
|
|
49
49
|
|
|
50
|
+
// package.json
|
|
51
|
+
var package_default = {
|
|
52
|
+
name: "@flotrace/runtime",
|
|
53
|
+
version: "2.2.3",
|
|
54
|
+
description: "Runtime package for FloTrace - enables real-time render tracking in your React app",
|
|
55
|
+
main: "./dist/index.js",
|
|
56
|
+
module: "./dist/index.mjs",
|
|
57
|
+
types: "./dist/index.d.ts",
|
|
58
|
+
exports: {
|
|
59
|
+
".": {
|
|
60
|
+
types: "./dist/index.d.ts",
|
|
61
|
+
import: "./dist/index.mjs",
|
|
62
|
+
require: "./dist/index.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
files: [
|
|
66
|
+
"dist",
|
|
67
|
+
"LICENSE",
|
|
68
|
+
"README.md"
|
|
69
|
+
],
|
|
70
|
+
scripts: {
|
|
71
|
+
build: "tsup",
|
|
72
|
+
dev: "tsup --watch",
|
|
73
|
+
typecheck: "tsc --noEmit",
|
|
74
|
+
clean: "rm -rf dist",
|
|
75
|
+
prepublishOnly: "npm run -w @flotrace/runtime-core build && npm run clean && npm run build && npm run typecheck",
|
|
76
|
+
"release:patch": "npm version patch && npm publish",
|
|
77
|
+
"release:minor": "npm version minor && npm publish",
|
|
78
|
+
"release:major": "npm version major && npm publish"
|
|
79
|
+
},
|
|
80
|
+
dependencies: {
|
|
81
|
+
"@flotrace/runtime-core": "2.2.3"
|
|
82
|
+
},
|
|
83
|
+
peerDependencies: {
|
|
84
|
+
react: ">=16.9.0",
|
|
85
|
+
"@types/react": ">=16.9.0"
|
|
86
|
+
},
|
|
87
|
+
peerDependenciesMeta: {
|
|
88
|
+
"@types/react": {
|
|
89
|
+
optional: true
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
devDependencies: {
|
|
93
|
+
tsup: "^8.0.0",
|
|
94
|
+
typescript: "^5.3.0"
|
|
95
|
+
},
|
|
96
|
+
keywords: [
|
|
97
|
+
"react",
|
|
98
|
+
"devtools",
|
|
99
|
+
"profiler",
|
|
100
|
+
"render",
|
|
101
|
+
"performance",
|
|
102
|
+
"flotrace"
|
|
103
|
+
],
|
|
104
|
+
license: "MIT",
|
|
105
|
+
homepage: "https://flotrace.dev",
|
|
106
|
+
repository: {
|
|
107
|
+
type: "git",
|
|
108
|
+
url: "https://github.com/sameersitre/runtime.git"
|
|
109
|
+
},
|
|
110
|
+
bugs: {
|
|
111
|
+
url: "https://github.com/sameersitre/runtime/issues"
|
|
112
|
+
},
|
|
113
|
+
publishConfig: {
|
|
114
|
+
access: "public"
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
50
118
|
// src/routerTracker.ts
|
|
51
119
|
var isInstalled = false;
|
|
52
120
|
var debounceTimer = null;
|
|
@@ -224,6 +292,7 @@ var requestCounter = 0;
|
|
|
224
292
|
var requestIndexMap = /* @__PURE__ */ new Map();
|
|
225
293
|
var earlyRequestIndexMap = /* @__PURE__ */ new Map();
|
|
226
294
|
var previousFetch = null;
|
|
295
|
+
var trackedFetchRef = null;
|
|
227
296
|
var originalXhrOpen = null;
|
|
228
297
|
var originalXhrSend = null;
|
|
229
298
|
var originalResponseJson = null;
|
|
@@ -265,8 +334,11 @@ function installNetworkTracker(wsClient) {
|
|
|
265
334
|
function uninstallNetworkTracker() {
|
|
266
335
|
if (!isInstalled2 && !isPrewarmed) return;
|
|
267
336
|
if (previousFetch) {
|
|
268
|
-
globalThis.fetch
|
|
337
|
+
if (globalThis.fetch === trackedFetchRef) {
|
|
338
|
+
globalThis.fetch = previousFetch;
|
|
339
|
+
}
|
|
269
340
|
previousFetch = null;
|
|
341
|
+
trackedFetchRef = null;
|
|
270
342
|
}
|
|
271
343
|
if (originalXhrOpen) {
|
|
272
344
|
XMLHttpRequest.prototype.open = originalXhrOpen;
|
|
@@ -304,10 +376,14 @@ function uninstallNetworkTracker() {
|
|
|
304
376
|
function patchFetch() {
|
|
305
377
|
if (typeof globalThis.fetch !== "function") return;
|
|
306
378
|
previousFetch = globalThis.fetch;
|
|
307
|
-
|
|
379
|
+
const capturedPreviousFetch = previousFetch;
|
|
380
|
+
const trackedFetch = async function trackedFetch2(input, init) {
|
|
381
|
+
if (!isInstalled2 && !isPrewarmed) {
|
|
382
|
+
return capturedPreviousFetch.call(globalThis, input, init);
|
|
383
|
+
}
|
|
308
384
|
const url = extractUrl(input);
|
|
309
385
|
if (isNoiseUrl(url)) {
|
|
310
|
-
return
|
|
386
|
+
return capturedPreviousFetch.call(globalThis, input, init);
|
|
311
387
|
}
|
|
312
388
|
const method = (init?.method ?? "GET").toUpperCase();
|
|
313
389
|
const parsedUrl = parseUrl(url);
|
|
@@ -322,7 +398,7 @@ function patchFetch() {
|
|
|
322
398
|
}
|
|
323
399
|
pushEntry({ ...entry });
|
|
324
400
|
try {
|
|
325
|
-
const response = await
|
|
401
|
+
const response = await capturedPreviousFetch.call(globalThis, input, init);
|
|
326
402
|
if (entry.state !== "aborted") {
|
|
327
403
|
entry.state = response.ok ? "success" : "error";
|
|
328
404
|
entry.status = response.status;
|
|
@@ -345,6 +421,8 @@ function patchFetch() {
|
|
|
345
421
|
throw err;
|
|
346
422
|
}
|
|
347
423
|
};
|
|
424
|
+
trackedFetchRef = trackedFetch;
|
|
425
|
+
globalThis.fetch = trackedFetch;
|
|
348
426
|
}
|
|
349
427
|
function patchXhr() {
|
|
350
428
|
if (typeof XMLHttpRequest === "undefined") return;
|
|
@@ -571,6 +649,7 @@ function flushBuffer() {
|
|
|
571
649
|
|
|
572
650
|
// src/FloTraceProvider.tsx
|
|
573
651
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
652
|
+
var RUNTIME_VERSION = package_default.version;
|
|
574
653
|
var pendingCleanupTimer = null;
|
|
575
654
|
function safeTrackerOp(name, op) {
|
|
576
655
|
try {
|
|
@@ -616,7 +695,8 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
|
|
|
616
695
|
appName: config.appName ?? deriveWebAppName(),
|
|
617
696
|
appId: config.appId ?? deriveWebAppId(),
|
|
618
697
|
frameworkName: config.frameworkName ?? framework.frameworkName,
|
|
619
|
-
frameworkVersion: config.frameworkVersion ?? framework.frameworkVersion
|
|
698
|
+
frameworkVersion: config.frameworkVersion ?? framework.frameworkVersion,
|
|
699
|
+
runtimeVersion: config.runtimeVersion ?? RUNTIME_VERSION
|
|
620
700
|
};
|
|
621
701
|
const [connected, setConnected] = import_react.default.useState(false);
|
|
622
702
|
const trackingOptionsRef = (0, import_react.useRef)({});
|
package/dist/index.mjs
CHANGED
|
@@ -30,6 +30,74 @@ import {
|
|
|
30
30
|
resolveValueTrace
|
|
31
31
|
} from "@flotrace/runtime-core";
|
|
32
32
|
|
|
33
|
+
// package.json
|
|
34
|
+
var package_default = {
|
|
35
|
+
name: "@flotrace/runtime",
|
|
36
|
+
version: "2.2.3",
|
|
37
|
+
description: "Runtime package for FloTrace - enables real-time render tracking in your React app",
|
|
38
|
+
main: "./dist/index.js",
|
|
39
|
+
module: "./dist/index.mjs",
|
|
40
|
+
types: "./dist/index.d.ts",
|
|
41
|
+
exports: {
|
|
42
|
+
".": {
|
|
43
|
+
types: "./dist/index.d.ts",
|
|
44
|
+
import: "./dist/index.mjs",
|
|
45
|
+
require: "./dist/index.js"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
files: [
|
|
49
|
+
"dist",
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"README.md"
|
|
52
|
+
],
|
|
53
|
+
scripts: {
|
|
54
|
+
build: "tsup",
|
|
55
|
+
dev: "tsup --watch",
|
|
56
|
+
typecheck: "tsc --noEmit",
|
|
57
|
+
clean: "rm -rf dist",
|
|
58
|
+
prepublishOnly: "npm run -w @flotrace/runtime-core build && npm run clean && npm run build && npm run typecheck",
|
|
59
|
+
"release:patch": "npm version patch && npm publish",
|
|
60
|
+
"release:minor": "npm version minor && npm publish",
|
|
61
|
+
"release:major": "npm version major && npm publish"
|
|
62
|
+
},
|
|
63
|
+
dependencies: {
|
|
64
|
+
"@flotrace/runtime-core": "2.2.3"
|
|
65
|
+
},
|
|
66
|
+
peerDependencies: {
|
|
67
|
+
react: ">=16.9.0",
|
|
68
|
+
"@types/react": ">=16.9.0"
|
|
69
|
+
},
|
|
70
|
+
peerDependenciesMeta: {
|
|
71
|
+
"@types/react": {
|
|
72
|
+
optional: true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
devDependencies: {
|
|
76
|
+
tsup: "^8.0.0",
|
|
77
|
+
typescript: "^5.3.0"
|
|
78
|
+
},
|
|
79
|
+
keywords: [
|
|
80
|
+
"react",
|
|
81
|
+
"devtools",
|
|
82
|
+
"profiler",
|
|
83
|
+
"render",
|
|
84
|
+
"performance",
|
|
85
|
+
"flotrace"
|
|
86
|
+
],
|
|
87
|
+
license: "MIT",
|
|
88
|
+
homepage: "https://flotrace.dev",
|
|
89
|
+
repository: {
|
|
90
|
+
type: "git",
|
|
91
|
+
url: "https://github.com/sameersitre/runtime.git"
|
|
92
|
+
},
|
|
93
|
+
bugs: {
|
|
94
|
+
url: "https://github.com/sameersitre/runtime/issues"
|
|
95
|
+
},
|
|
96
|
+
publishConfig: {
|
|
97
|
+
access: "public"
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
33
101
|
// src/routerTracker.ts
|
|
34
102
|
var isInstalled = false;
|
|
35
103
|
var debounceTimer = null;
|
|
@@ -213,6 +281,7 @@ var requestCounter = 0;
|
|
|
213
281
|
var requestIndexMap = /* @__PURE__ */ new Map();
|
|
214
282
|
var earlyRequestIndexMap = /* @__PURE__ */ new Map();
|
|
215
283
|
var previousFetch = null;
|
|
284
|
+
var trackedFetchRef = null;
|
|
216
285
|
var originalXhrOpen = null;
|
|
217
286
|
var originalXhrSend = null;
|
|
218
287
|
var originalResponseJson = null;
|
|
@@ -254,8 +323,11 @@ function installNetworkTracker(wsClient) {
|
|
|
254
323
|
function uninstallNetworkTracker() {
|
|
255
324
|
if (!isInstalled2 && !isPrewarmed) return;
|
|
256
325
|
if (previousFetch) {
|
|
257
|
-
globalThis.fetch
|
|
326
|
+
if (globalThis.fetch === trackedFetchRef) {
|
|
327
|
+
globalThis.fetch = previousFetch;
|
|
328
|
+
}
|
|
258
329
|
previousFetch = null;
|
|
330
|
+
trackedFetchRef = null;
|
|
259
331
|
}
|
|
260
332
|
if (originalXhrOpen) {
|
|
261
333
|
XMLHttpRequest.prototype.open = originalXhrOpen;
|
|
@@ -293,10 +365,14 @@ function uninstallNetworkTracker() {
|
|
|
293
365
|
function patchFetch() {
|
|
294
366
|
if (typeof globalThis.fetch !== "function") return;
|
|
295
367
|
previousFetch = globalThis.fetch;
|
|
296
|
-
|
|
368
|
+
const capturedPreviousFetch = previousFetch;
|
|
369
|
+
const trackedFetch = async function trackedFetch2(input, init) {
|
|
370
|
+
if (!isInstalled2 && !isPrewarmed) {
|
|
371
|
+
return capturedPreviousFetch.call(globalThis, input, init);
|
|
372
|
+
}
|
|
297
373
|
const url = extractUrl(input);
|
|
298
374
|
if (isNoiseUrl(url)) {
|
|
299
|
-
return
|
|
375
|
+
return capturedPreviousFetch.call(globalThis, input, init);
|
|
300
376
|
}
|
|
301
377
|
const method = (init?.method ?? "GET").toUpperCase();
|
|
302
378
|
const parsedUrl = parseUrl(url);
|
|
@@ -311,7 +387,7 @@ function patchFetch() {
|
|
|
311
387
|
}
|
|
312
388
|
pushEntry({ ...entry });
|
|
313
389
|
try {
|
|
314
|
-
const response = await
|
|
390
|
+
const response = await capturedPreviousFetch.call(globalThis, input, init);
|
|
315
391
|
if (entry.state !== "aborted") {
|
|
316
392
|
entry.state = response.ok ? "success" : "error";
|
|
317
393
|
entry.status = response.status;
|
|
@@ -334,6 +410,8 @@ function patchFetch() {
|
|
|
334
410
|
throw err;
|
|
335
411
|
}
|
|
336
412
|
};
|
|
413
|
+
trackedFetchRef = trackedFetch;
|
|
414
|
+
globalThis.fetch = trackedFetch;
|
|
337
415
|
}
|
|
338
416
|
function patchXhr() {
|
|
339
417
|
if (typeof XMLHttpRequest === "undefined") return;
|
|
@@ -560,6 +638,7 @@ function flushBuffer() {
|
|
|
560
638
|
|
|
561
639
|
// src/FloTraceProvider.tsx
|
|
562
640
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
641
|
+
var RUNTIME_VERSION = package_default.version;
|
|
563
642
|
var pendingCleanupTimer = null;
|
|
564
643
|
function safeTrackerOp(name, op) {
|
|
565
644
|
try {
|
|
@@ -605,7 +684,8 @@ function FloTraceProvider({ children, config = {}, stores, reduxStore, queryClie
|
|
|
605
684
|
appName: config.appName ?? deriveWebAppName(),
|
|
606
685
|
appId: config.appId ?? deriveWebAppId(),
|
|
607
686
|
frameworkName: config.frameworkName ?? framework.frameworkName,
|
|
608
|
-
frameworkVersion: config.frameworkVersion ?? framework.frameworkVersion
|
|
687
|
+
frameworkVersion: config.frameworkVersion ?? framework.frameworkVersion,
|
|
688
|
+
runtimeVersion: config.runtimeVersion ?? RUNTIME_VERSION
|
|
609
689
|
};
|
|
610
690
|
const [connected, setConnected] = React.useState(false);
|
|
611
691
|
const trackingOptionsRef = useRef({});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flotrace/runtime",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "Runtime package for FloTrace - enables real-time render tracking in your React app",
|
|
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",
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
"release:major": "npm version major && npm publish"
|
|
27
29
|
},
|
|
28
30
|
"dependencies": {
|
|
29
|
-
"@flotrace/runtime-core": "2.2.
|
|
31
|
+
"@flotrace/runtime-core": "2.2.3"
|
|
30
32
|
},
|
|
31
33
|
"peerDependencies": {
|
|
32
34
|
"react": ">=16.9.0",
|
|
@@ -50,10 +52,13 @@
|
|
|
50
52
|
"flotrace"
|
|
51
53
|
],
|
|
52
54
|
"license": "MIT",
|
|
55
|
+
"homepage": "https://flotrace.dev",
|
|
53
56
|
"repository": {
|
|
54
57
|
"type": "git",
|
|
55
|
-
"url": "https://github.com/
|
|
56
|
-
|
|
58
|
+
"url": "https://github.com/sameersitre/runtime.git"
|
|
59
|
+
},
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/sameersitre/runtime/issues"
|
|
57
62
|
},
|
|
58
63
|
"publishConfig": {
|
|
59
64
|
"access": "public"
|