nuxt-devtools-observatory 0.1.32 → 0.1.34

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.
Files changed (36) hide show
  1. package/README.md +37 -1
  2. package/client/.env.example +2 -0
  3. package/client/dist/assets/index-BO7neKEi.css +1 -0
  4. package/client/dist/assets/index-fFBuk6M6.js +20 -0
  5. package/client/dist/index.html +2 -2
  6. package/client/src/App.vue +8 -0
  7. package/client/src/components/Flamegraph.vue +4 -4
  8. package/client/src/components/SpanInspector.vue +1 -1
  9. package/client/src/composables/composable-search.ts +3 -0
  10. package/client/src/composables/trace-render-aggregation.ts +11 -2
  11. package/client/src/composables/useVirtualizationConfig.ts +40 -0
  12. package/client/src/composables/useVirtualizationFlags.ts +129 -0
  13. package/client/src/stores/observatory.ts +20 -0
  14. package/client/src/views/ComposableTracker.vue +212 -71
  15. package/client/src/views/FetchDashboard.vue +181 -16
  16. package/client/src/views/PiniaStoreTracker.vue +343 -0
  17. package/client/src/views/ProvideInjectGraph.vue +66 -18
  18. package/client/src/views/RenderHeatmap.vue +329 -75
  19. package/client/src/views/TraceViewer.vue +190 -20
  20. package/client/src/views/TransitionTimeline.vue +112 -19
  21. package/dist/module.d.mts +15 -0
  22. package/dist/module.json +1 -1
  23. package/dist/module.mjs +28 -24
  24. package/dist/runtime/composables/pinia-store-registry.d.ts +44 -0
  25. package/dist/runtime/composables/pinia-store-registry.js +447 -0
  26. package/dist/runtime/composables/provide-inject-registry.js +13 -8
  27. package/dist/runtime/composables/render-registry.js +6 -4
  28. package/dist/runtime/instrumentation/asyncData.d.ts +1 -1
  29. package/dist/runtime/instrumentation/fetch.d.ts +7 -1
  30. package/dist/runtime/instrumentation/fetch.js +22 -1
  31. package/dist/runtime/plugin.js +39 -2
  32. package/dist/runtime/test-bridge.d.ts +18 -0
  33. package/dist/runtime/test-bridge.js +100 -0
  34. package/package.json +14 -3
  35. package/client/dist/assets/index-5Wl1XYRH.js +0 -17
  36. package/client/dist/assets/index-DT_QUiIh.css +0 -1
@@ -97,27 +97,46 @@ function matchesSearch(node: TreeNodeData, query: string): boolean {
97
97
  }
98
98
 
99
99
  /**
100
- * Count leaf nodes in a subtree iteratively to avoid stack overflow on
101
- * pathologically deep provide/inject trees (e.g. every component re-providing
102
- * the same key creates a chain as long as the component tree itself).
103
- * @param {TreeNodeData} root - The root node of the subtree to count leaves for.
104
- * @returns {number} The number of leaf nodes in the subtree.
100
+ * Build a leaf-count lookup for each node in visible trees.
101
+ * Uses iterative post-order traversal to keep deep trees stack-safe.
102
+ * @param {TreeNodeData[]} roots - Visible tree roots.
103
+ * @returns {Map<string, number>} Map of node id to subtree leaf count.
105
104
  */
106
- function countLeaves(root: TreeNodeData): number {
107
- let count = 0
108
- const stack: TreeNodeData[] = [root]
105
+ function buildLeafCountMap(roots: TreeNodeData[]): Map<string, number> {
106
+ const counts = new Map<string, number>()
109
107
 
110
- while (stack.length) {
111
- const node = stack.pop()!
108
+ for (const root of roots) {
109
+ const stack: Array<{ node: TreeNodeData; visited: boolean }> = [{ node: root, visited: false }]
112
110
 
113
- if (node.children.length === 0) {
114
- count++
115
- } else {
116
- stack.push(...node.children)
111
+ while (stack.length) {
112
+ const current = stack.pop()!
113
+
114
+ if (!current.visited) {
115
+ stack.push({ node: current.node, visited: true })
116
+
117
+ for (let i = current.node.children.length - 1; i >= 0; i--) {
118
+ stack.push({ node: current.node.children[i], visited: false })
119
+ }
120
+
121
+ continue
122
+ }
123
+
124
+ if (current.node.children.length === 0) {
125
+ counts.set(current.node.id, 1)
126
+ continue
127
+ }
128
+
129
+ let total = 0
130
+
131
+ for (const child of current.node.children) {
132
+ total += counts.get(child.id) ?? 1
133
+ }
134
+
135
+ counts.set(current.node.id, total)
117
136
  }
118
137
  }
119
138
 
120
- return count
139
+ return counts
121
140
  }
122
141
 
123
142
  function stringifyValue(value: unknown) {
@@ -359,6 +378,24 @@ const visibleNodes = computed<TreeNodeData[]>(() => {
359
378
  return nodes.value.map(pruneIterative).filter(Boolean) as TreeNodeData[]
360
379
  })
361
380
 
381
+ const visibleLeafCountById = computed(() => buildLeafCountMap(visibleNodes.value))
382
+
383
+ function findNodeById(roots: TreeNodeData[], id: string): TreeNodeData | null {
384
+ const stack = [...roots]
385
+
386
+ while (stack.length) {
387
+ const node = stack.pop()!
388
+
389
+ if (node.id === id) {
390
+ return node
391
+ }
392
+
393
+ stack.push(...node.children)
394
+ }
395
+
396
+ return null
397
+ }
398
+
362
399
  watch([visibleNodes, selectedNode], ([currentNodes, currentSelected]) => {
363
400
  if (!currentSelected) {
364
401
  return
@@ -375,12 +412,23 @@ watch([visibleNodes, selectedNode], ([currentNodes, currentSelected]) => {
375
412
 
376
413
  if (!ids.has(currentSelected.id)) {
377
414
  selectedNode.value = null
415
+
416
+ return
417
+ }
418
+
419
+ // Keep details reactive: remap to the latest node instance after snapshots refresh.
420
+ const latest = findNodeById(currentNodes, currentSelected.id)
421
+
422
+ if (latest && latest !== currentSelected) {
423
+ selectedNode.value = latest
378
424
  }
379
425
  })
380
426
 
381
427
  const layout = computed<LayoutNode[]>(() => {
382
428
  const flat: LayoutNode[] = []
383
429
  const pad = H_GAP
430
+ const leafCountById = visibleLeafCountById.value
431
+ const getLeafCount = (node: TreeNodeData) => leafCountById.get(node.id) ?? 1
384
432
 
385
433
  // Iterative replacement for the recursive place() — avoids stack overflow
386
434
  // on deep component trees. Uses an explicit stack of pending work items.
@@ -398,7 +446,7 @@ const layout = computed<LayoutNode[]>(() => {
398
446
 
399
447
  while (stack.length) {
400
448
  const { node, depth, slotLeft, parentId } = stack.pop()!
401
- const leaves = countLeaves(node)
449
+ const leaves = getLeafCount(node)
402
450
  const slotWidth = leaves * (NODE_W + H_GAP) - H_GAP
403
451
 
404
452
  flat.push({
@@ -413,7 +461,7 @@ const layout = computed<LayoutNode[]>(() => {
413
461
  const childWork: WorkItem[] = []
414
462
 
415
463
  for (const child of node.children) {
416
- const childLeaves = countLeaves(child)
464
+ const childLeaves = getLeafCount(child)
417
465
  childWork.push({ node: child, depth: depth + 1, slotLeft: childLeft, parentId: node.id })
418
466
  childLeft += childLeaves * (NODE_W + H_GAP)
419
467
  }
@@ -423,7 +471,7 @@ const layout = computed<LayoutNode[]>(() => {
423
471
  }
424
472
  }
425
473
 
426
- const rootLeaves = countLeaves(root)
474
+ const rootLeaves = getLeafCount(root)
427
475
  left += rootLeaves * (NODE_W + H_GAP) + H_GAP * 2
428
476
  }
429
477