nuxt-devtools-observatory 0.1.31 → 0.1.33
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 +79 -46
- package/client/.env.example +1 -0
- package/client/dist/assets/index-BqKYgjVB.js +20 -0
- package/client/dist/assets/index-bs1JBJ2u.css +1 -0
- package/client/dist/index.html +2 -2
- package/client/src/App.vue +4 -0
- package/client/src/components/Flamegraph.vue +7 -8
- package/client/src/components/SpanInspector.vue +1 -1
- package/client/src/components/TraceFilter.vue +0 -2
- package/client/src/components/WaterfallView.vue +1 -1
- package/client/src/composables/composable-search.ts +127 -0
- package/client/src/composables/trace-render-aggregation.ts +263 -0
- package/client/src/composables/useExportImport.ts +63 -0
- package/client/src/composables/useTraceFilter.ts +1 -5
- package/client/src/composables/useVirtualizationConfig.ts +40 -0
- package/client/src/composables/useVirtualizationFlags.ts +129 -0
- package/client/src/stores/observatory.ts +9 -1
- package/client/src/views/ComposableTracker.vue +273 -97
- package/client/src/views/FetchDashboard.vue +181 -16
- package/client/src/views/ProvideInjectGraph.vue +41 -18
- package/client/src/views/RenderHeatmap.vue +392 -76
- package/client/src/views/TraceViewer.vue +797 -14
- package/client/src/views/TransitionTimeline.vue +112 -19
- package/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +12 -23
- package/dist/runtime/composables/composable-registry.d.ts +19 -0
- package/dist/runtime/composables/composable-registry.js +63 -5
- package/dist/runtime/composables/render-registry.js +23 -13
- package/dist/runtime/instrumentation/asyncData.d.ts +1 -1
- package/dist/runtime/instrumentation/fetch.d.ts +7 -1
- package/dist/runtime/instrumentation/fetch.js +22 -1
- package/dist/runtime/nitro/fetch-capture.d.ts +1 -2
- package/dist/runtime/nitro/fetch-capture.js +85 -7
- package/dist/runtime/nitro/ssr-trace-store.d.ts +85 -0
- package/dist/runtime/nitro/ssr-trace-store.js +84 -0
- package/dist/runtime/plugin.js +48 -1
- package/dist/runtime/test-bridge.d.ts +18 -0
- package/dist/runtime/test-bridge.js +86 -0
- package/dist/runtime/tracing/trace.d.ts +1 -1
- package/package.json +18 -3
- package/client/.env +0 -17
- package/client/dist/assets/index-BuMXDBO9.js +0 -17
- package/client/dist/assets/index-CwcspZ6w.css +0 -1
package/README.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# Nuxt DevTools Observatory
|
|
4
4
|
|
|
5
|
+
> [!WARNING]
|
|
6
|
+
> **Performance note:** Instrumentation wraps every useFetch, composable, and lifecycle hook. In large apps this can slow down the DevTools UI or increase memory usage. Disable unused features via the observatory config.
|
|
7
|
+
|
|
5
8
|
Nuxt DevTools module providing six missing observability features:
|
|
6
9
|
|
|
7
10
|
- **useFetch Dashboard** — central view of all async data calls, cache keys, waterfall timeline
|
|
@@ -49,6 +52,7 @@ Options set in `nuxt.config.ts` take precedence over environment variables.
|
|
|
49
52
|
- `heatmapHideInternals` (boolean) — Hide node_modules and internal components in the render heatmap for a cleaner view
|
|
50
53
|
- `debugRpc` (boolean) — Enable RPC handshake debug logs in the Observatory iframe/host bridge (set via `OBSERVATORY_DEBUG_RPC`)
|
|
51
54
|
- `maxFetchEntries` (number) — Max fetch entries to keep in memory
|
|
55
|
+
- `fetchPageSize` (number) — Rows loaded per infinite-scroll step in the useFetch Dashboard (set via `OBSERVATORY_FETCH_PAGE_SIZE`)
|
|
52
56
|
- `maxPayloadBytes` (number) — Max payload size (bytes) per fetch entry
|
|
53
57
|
- `maxTransitions` (number) — Max transition entries to keep in memory
|
|
54
58
|
- `maxComposableHistory` (number) — Max composable history events per entry
|
|
@@ -77,6 +81,7 @@ export default defineNuxtConfig({
|
|
|
77
81
|
heatmapHideInternals: true, // Hide node_modules and internal components in the render heatmap
|
|
78
82
|
debugRpc: false, // Enable RPC handshake debug logs (useful for troubleshooting)
|
|
79
83
|
maxFetchEntries: 200, // Max fetch entries to keep in memory
|
|
84
|
+
fetchPageSize: 20, // Rows loaded per infinite-scroll step in the useFetch Dashboard
|
|
80
85
|
maxPayloadBytes: 10000, // Max payload size (bytes) per fetch entry
|
|
81
86
|
maxTransitions: 500, // Max transition entries to keep in memory
|
|
82
87
|
maxComposableHistory: 50, // Max composable history events per entry
|
|
@@ -112,6 +117,9 @@ A Vite plugin wraps `useFetch` / `useAsyncData` calls with a thin shim that reco
|
|
|
112
117
|
A Nitro plugin captures server-side fetch timing independently and tunnels it to the
|
|
113
118
|
client over the HMR WebSocket.
|
|
114
119
|
|
|
120
|
+
The table supports infinite-scroll paging with row virtualization. `fetchPageSize`
|
|
121
|
+
controls how many rows are added per scroll step.
|
|
122
|
+
|
|
115
123
|
### provide/inject Graph
|
|
116
124
|
|
|
117
125
|
[](https://github.com/victorlmneves/nuxt-devtools-observatory/blob/main/docs/screenshots/provide-inject-graph.png)
|
|
@@ -162,27 +170,22 @@ wraps them with a tracking shim (`__trackComposable`) that:
|
|
|
162
170
|
The panel provides:
|
|
163
171
|
|
|
164
172
|
- **Navigation mode toggle** — switch between 'route' mode (clears entries on navigation) and 'session' mode (persists entries across pages). In session mode, a "clear session" button appears to manually reset the history
|
|
165
|
-
- **Filtering** by status (all / mounted / unmounted / leaks only) and free-text search across composable name, source file, ref key names, and
|
|
173
|
+
- **Filtering** by status (all / mounted / unmounted / leaks only) and free-text search across composable name, source file, ref key names, ref values, and nested reactive object properties
|
|
166
174
|
- **Recency-first ordering** — newest entries appear first, with layout-level composables pinned to the top (layout composables persist across page navigation)
|
|
167
175
|
- **Inline ref chip preview** — up to three reactive values shown on the card without expanding, with distinct styling for `ref`, `computed`, and `reactive` types
|
|
168
176
|
- **Collapsible ref values** — long objects and arrays automatically collapse with a chevron toggle to expand them inline in the detail view with full pretty-printed JSON
|
|
169
177
|
- **Global state badges** — keys shared across instances are highlighted in amber with a `global` badge and an explanatory banner when expanded
|
|
170
178
|
- **Change history** — a scrollable log of the last 50 value mutations with key, new value, and relative timestamp
|
|
171
179
|
- **Lifecycle summary** — shows whether `onMounted`/`onUnmounted` were registered and whether watchers and intervals were properly cleaned up
|
|
172
|
-
- **Reverse lookup** — clicking any ref key opens a panel listing
|
|
180
|
+
- **Reverse lookup** — clicking any ref key opens a panel listing related composable instances. For shared/global refs it matches by shared object identity; otherwise it falls back to key-name matching
|
|
173
181
|
- **Inline value editing** — writable `ref` values have an `edit` button; clicking opens a JSON textarea that applies the new value directly to the live ref in the running app
|
|
174
182
|
- **Jump to editor** — an `open ↗` button in the context section opens the composable's source file in the configured editor
|
|
175
183
|
|
|
176
|
-
**Known gaps:**
|
|
177
|
-
|
|
178
|
-
- Reverse lookup matches by key name only, not by object identity
|
|
179
|
-
- Search does not look inside nested `reactive` object properties
|
|
180
|
-
|
|
181
184
|
### Render Heatmap
|
|
182
185
|
|
|
183
186
|
[](https://github.com/victorlmneves/nuxt-devtools-observatory/blob/main/docs/screenshots/render-heatmap.png)
|
|
184
187
|
|
|
185
|
-
Uses Vue
|
|
188
|
+
Uses Vue lifecycle instrumentation with `app.config.performance = true`.
|
|
186
189
|
Accurate duration is measured by bracketing each `beforeMount`/`mounted` and
|
|
187
190
|
`beforeUpdate`/`updated` cycle with `performance.now()` timestamps.
|
|
188
191
|
Component bounding boxes are captured via `$el.getBoundingClientRect()` for the DOM
|
|
@@ -194,8 +197,7 @@ last). Every mount and update cycle appends an event recording:
|
|
|
194
197
|
- `kind` — `mount` or `update`
|
|
195
198
|
- `t` — `performance.now()` timestamp
|
|
196
199
|
- `durationMs` — measured render duration
|
|
197
|
-
- `triggerKey` —
|
|
198
|
-
before `updated`), formatted as `type: key`
|
|
200
|
+
- `triggerKey` — optional reactive dep label for the update (when available)
|
|
199
201
|
- `route` — the route path at the time of the render
|
|
200
202
|
|
|
201
203
|
A `route` field on each entry records which route the component was first seen on.
|
|
@@ -224,6 +226,11 @@ The panel provides:
|
|
|
224
226
|
panel's identity section has an `open ↗` button; both call Vite's built-in
|
|
225
227
|
`/__open-in-editor` endpoint to open the component's source file in the configured
|
|
226
228
|
editor
|
|
229
|
+
- **Export / Import** — `↓ export` downloads the current render data (live or frozen
|
|
230
|
+
snapshot) as a `.json` file; `↑ import` loads a previously exported file and
|
|
231
|
+
automatically enters freeze mode so the imported snapshot is displayed in place of
|
|
232
|
+
live data; the freeze button label changes to `unfreeze (imported)` to signal the
|
|
233
|
+
import state
|
|
227
234
|
|
|
228
235
|
**Known gaps:**
|
|
229
236
|
|
|
@@ -241,14 +248,15 @@ after a route change.
|
|
|
241
248
|
|
|
242
249
|
**Span types collected:**
|
|
243
250
|
|
|
244
|
-
| Type | Emitted by | What it measures
|
|
245
|
-
| ------------ | --------------------------------- |
|
|
246
|
-
| `navigation` | `router.afterEach` hook | Route change from → to, duration
|
|
247
|
-
| `component` | Vue mixin lifecycle hooks | Exact `mounted` / `updated` hook cost
|
|
248
|
-
| `render` | `beforeMount` → `mounted` bracket | Real DOM-patching time per component mount/update
|
|
249
|
-
| `fetch` | `useFetch` / `useAsyncData` shim | Network request start, server/client origin, latency
|
|
250
|
-
| `
|
|
251
|
-
| `
|
|
251
|
+
| Type | Emitted by | What it measures |
|
|
252
|
+
| ------------ | --------------------------------- | ------------------------------------------------------ |
|
|
253
|
+
| `navigation` | `router.afterEach` hook | Route change from → to, duration |
|
|
254
|
+
| `component` | Vue mixin lifecycle hooks | Exact `mounted` / `updated` hook cost |
|
|
255
|
+
| `render` | `beforeMount` → `mounted` bracket | Real DOM-patching time per component mount/update |
|
|
256
|
+
| `fetch` | `useFetch` / `useAsyncData` shim | Network request start, server/client origin, latency |
|
|
257
|
+
| `server` | Nitro SSR lifecycle hooks | Server-side phase timing (e.g. `render:html`) |
|
|
258
|
+
| `composable` | `__trackComposable` shim | Setup phase of tracked `useXxx()` calls (client + SSR) |
|
|
259
|
+
| `transition` | `<Transition>` wrapper | Full enter/leave lifecycle phase |
|
|
252
260
|
|
|
253
261
|
**Render span tracking:**
|
|
254
262
|
Real render time is measured by storing `performance.now()` in a `WeakMap<ComponentPublicInstance, number>` inside `beforeMount` / `beforeUpdate`, then reading it back in the corresponding `mounted` / `updated` hooks. This produces a `type: 'render'` span whose duration is the actual DOM-patching cost, separately from the `component:mounted` hook span (which only measures the hook body itself).
|
|
@@ -265,6 +273,14 @@ The panel provides:
|
|
|
265
273
|
- **Filter panel** — filter by span type and free-text search across span names and metadata
|
|
266
274
|
- **Duration labels** — spans narrower than 5 % of the timeline render their label outside the bar for readability
|
|
267
275
|
- **In-progress traces** — active traces show `~Xms` (computed from the latest span end offset) rather than a hard "in progress" label
|
|
276
|
+
- **Export / Import** — `↓ export` downloads all captured traces as a `.json` file
|
|
277
|
+
(always exports the full unfiltered dataset); `↑ import` loads a previously exported
|
|
278
|
+
file and freezes the view on the imported data; a `← live` button returns to the
|
|
279
|
+
live stream; the trace count label shows `(imported)` while viewing imported data
|
|
280
|
+
- **Cross-trace render comparison** — compares render behavior across filtered traces,
|
|
281
|
+
with per-component average re-renders per trace, selected-trace value, and delta
|
|
282
|
+
versus baseline; includes sortable columns, quick filters (regressions/comparable),
|
|
283
|
+
in-table search, and condensed mobile columns
|
|
268
284
|
|
|
269
285
|
**What it tells you:**
|
|
270
286
|
|
|
@@ -274,11 +290,6 @@ The panel provides:
|
|
|
274
290
|
- Whether composable setup is adding meaningful latency
|
|
275
291
|
- Which components are slow to mount and might benefit from `<Suspense>` or lazy loading
|
|
276
292
|
|
|
277
|
-
**Known gaps:**
|
|
278
|
-
|
|
279
|
-
- SSR spans are not yet captured — only client-side navigation traces are collected
|
|
280
|
-
- Re-render counts are tracked by Render Heatmap; the Trace Viewer records individual render events but does not aggregate them
|
|
281
|
-
|
|
282
293
|
### Transition Tracker
|
|
283
294
|
|
|
284
295
|
[](https://github.com/victorlmneves/nuxt-devtools-observatory/blob/main/docs/screenshots/transition-tracker.png)
|
|
@@ -324,16 +335,7 @@ const result = useMyComposable()
|
|
|
324
335
|
|
|
325
336
|
## Roadmap
|
|
326
337
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
- [ ] Reverse lookup by object identity rather than key name only
|
|
330
|
-
- [ ] Deep search inside nested `reactive` object properties
|
|
331
|
-
|
|
332
|
-
### Trace Viewer
|
|
333
|
-
|
|
334
|
-
- [ ] SSR span collection (server-side navigation and composable setup)
|
|
335
|
-
- [ ] Cross-trace comparison view
|
|
336
|
-
- [ ] Export traces as JSON
|
|
338
|
+
- [x] Dedicated cross-trace comparison UI polish (sorting, stronger visual deltas, and quick filtering)
|
|
337
339
|
|
|
338
340
|
## Development
|
|
339
341
|
|
|
@@ -373,16 +375,26 @@ src/
|
|
|
373
375
|
│ ├── provide-inject-transform.ts ← AST wraps provide/inject
|
|
374
376
|
│ ├── composable-transform.ts ← AST wraps useX() composables
|
|
375
377
|
│ └── transition-transform.ts ← Virtual vue proxy — overrides Transition export
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
│
|
|
380
|
-
│
|
|
381
|
-
│
|
|
382
|
-
│
|
|
383
|
-
│
|
|
384
|
-
|
|
385
|
-
|
|
378
|
+
└── runtime/
|
|
379
|
+
├── plugin.ts ← Client runtime bootstrap + RPC/Vite WS bridge
|
|
380
|
+
├── composables/
|
|
381
|
+
│ ├── fetch-registry.ts ← Fetch tracking store + __devFetch shim
|
|
382
|
+
│ ├── provide-inject-registry.ts ← Injection tracking + __devProvide/__devInject
|
|
383
|
+
│ ├── composable-registry.ts ← Composable tracking + __trackComposable + leak detection
|
|
384
|
+
│ ├── render-registry.ts ← Render registry (timeline, route attribution, bbox snapshots)
|
|
385
|
+
│ └── transition-registry.ts ← Transition lifecycle store
|
|
386
|
+
├── instrumentation/
|
|
387
|
+
│ ├── route.ts ← router.afterEach hook — opens/closes traces per navigation
|
|
388
|
+
│ ├── component.ts ← Vue mixin lifecycle hooks — component + render spans
|
|
389
|
+
│ ├── fetch.ts ← useFetch/useAsyncData span recording
|
|
390
|
+
│ └── asyncData.ts ← useAsyncData-specific span handling
|
|
391
|
+
├── tracing/
|
|
392
|
+
│ ├── trace.ts ← Span + Trace types (server-side internal)
|
|
393
|
+
│ ├── traceStore.ts ← In-memory Map<string, Trace> store
|
|
394
|
+
│ ├── tracing.ts ← Span open/close helpers
|
|
395
|
+
│ └── context.ts ← Current-trace context (per async task)
|
|
396
|
+
└── nitro/
|
|
397
|
+
└── fetch-capture.ts ← SSR request tracing bridge (fetch + server phases + context)
|
|
386
398
|
|
|
387
399
|
client/
|
|
388
400
|
├── index.html
|
|
@@ -393,31 +405,52 @@ client/
|
|
|
393
405
|
├── main.ts
|
|
394
406
|
├── style.css ← Design system
|
|
395
407
|
├── components/
|
|
408
|
+
├── composables/
|
|
409
|
+
│ ├── composable-search.ts ← Deep nested composable search matching utilities
|
|
410
|
+
│ ├── trace-render-aggregation.ts ← Per-trace and cross-trace render aggregation helpers
|
|
411
|
+
│ ├── useExportImport.ts ← JSON export (download) and import (file picker) utilities
|
|
412
|
+
│ ├── useResizablePane.ts ← Resizable split-pane drag handle
|
|
413
|
+
│ └── useTraceFilter.ts ← Trace filter state and filtering logic
|
|
396
414
|
├── stores/
|
|
397
415
|
└── views/
|
|
398
416
|
├── FetchDashboard.vue ← useFetch tab UI
|
|
399
417
|
├── ProvideInjectGraph.vue ← provide/inject tab UI
|
|
400
418
|
├── ComposableTracker.vue ← Composable tab UI
|
|
419
|
+
├── ComponentBlock.vue ← Shared component detail block
|
|
420
|
+
├── ValueInspector.vue ← Inline JSON value inspector
|
|
401
421
|
├── RenderHeatmap.vue ← Heatmap tab UI
|
|
402
422
|
├── TransitionTimeline.vue ← Transition tracker tab UI
|
|
403
|
-
└── TraceViewer.vue ← Trace viewer tab UI (Flamegraph + Waterfall + Inspector)
|
|
423
|
+
└── TraceViewer.vue ← Trace viewer tab UI (Overview + Flamegraph + Waterfall + Inspector + cross-trace comparison)
|
|
404
424
|
|
|
405
425
|
playground/
|
|
406
426
|
├── app.vue ← Demo app shell used during local development
|
|
407
427
|
├── nuxt.config.ts
|
|
408
428
|
├── composables/
|
|
409
429
|
│ ├── useCounter.ts ← Clean composable (properly cleaned up)
|
|
410
|
-
│
|
|
430
|
+
│ ├── useLeakyPoller.ts ← Intentionally leaky (for demo)
|
|
431
|
+
│ ├── useCart.ts ← Cart state composable
|
|
432
|
+
│ ├── useDashboard.ts ← Dashboard data composable
|
|
433
|
+
│ ├── useLeakyCartAudit.ts ← Leaky audit poller (for demo)
|
|
434
|
+
│ ├── usePersistentCartSummary.ts ← Cart summary across navigations
|
|
435
|
+
│ ├── useProductFilter.ts ← Product search/filter state
|
|
436
|
+
│ └── useUserPreferences.ts ← User preferences store
|
|
411
437
|
├── components/
|
|
412
438
|
│ ├── ThemeConsumer.vue ← Successfully injects 'theme'
|
|
413
439
|
│ ├── MissingProviderConsumer.vue ← Injects 'cartContext' (no provider — red node)
|
|
414
440
|
│ ├── LeakyComponent.vue ← Mounts useLeakyPoller
|
|
441
|
+
│ ├── LeakyCartMonitor.vue ← Mounts useLeakyCartAudit
|
|
415
442
|
│ ├── HeavyList.vue ← Re-renders on every shuffle (heatmap demo)
|
|
416
443
|
│ ├── PriceDisplay.vue ← Leaf component with high render count
|
|
444
|
+
│ ├── CartDrawer.vue ← Cart sidebar
|
|
445
|
+
│ ├── CartItem.vue ← Individual cart item row
|
|
446
|
+
│ ├── DataTable.vue ← Generic sortable table
|
|
447
|
+
│ ├── SettingsPanel.vue ← Settings form panel
|
|
448
|
+
│ ├── StatsCard.vue ← Stat metric card
|
|
417
449
|
│ └── transitions/
|
|
418
450
|
│ ├── FadeBox.vue ← Healthy enter/leave transition
|
|
419
451
|
│ ├── BrokenTransition.vue ← Missing CSS classes (enter fires but stays in entering)
|
|
420
|
-
│
|
|
452
|
+
│ ├── CancelledTransition.vue ← Rapid toggle triggers enter-cancelled / leave-cancelled
|
|
453
|
+
│ └── InterruptedTransition.vue ← Back-to-back transitions trigger interrupted phase
|
|
421
454
|
└── server/api/
|
|
422
455
|
└── product.ts ← Mock API endpoint
|
|
423
456
|
|
package/client/.env.example
CHANGED
|
@@ -6,6 +6,7 @@ VITE_OBSERVATORY_RENDER_HEATMAP=true
|
|
|
6
6
|
VITE_OBSERVATORY_TRANSITION_TRACKER=true
|
|
7
7
|
VITE_OBSERVATORY_TRACE_VIEWER=true
|
|
8
8
|
VITE_OBSERVATORY_MAX_FETCH_ENTRIES=200
|
|
9
|
+
VITE_OBSERVATORY_FETCH_PAGE_SIZE=20
|
|
9
10
|
VITE_OBSERVATORY_MAX_PAYLOAD_BYTES=10000
|
|
10
11
|
VITE_OBSERVATORY_MAX_TRANSITIONS=500
|
|
11
12
|
VITE_OBSERVATORY_MAX_COMPOSABLE_HISTORY=50
|