vite-devtools-svelte 0.2.0 → 0.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # vite-devtools-svelte
2
2
 
3
- Svelte DevTools plugin for [Vite DevTools](https://github.com/nicepkg/vite-devtools). Provides 15 specialized panels for debugging, profiling, and inspecting Svelte/SvelteKit applications — all integrated directly into the Vite DevTools UI.
3
+ Svelte DevTools plugin for [Vite DevTools](https://github.com/vitejs/devtools). Provides 15 specialized panels for debugging, profiling, and inspecting Svelte/SvelteKit applications — all integrated directly into the Vite DevTools UI.
4
4
 
5
5
  > **Status:** Early development. APIs may change.
6
6
 
@@ -135,25 +135,33 @@ Svelte DevTools plugin for [Vite DevTools](https://github.com/nicepkg/vite-devto
135
135
  - **Vite** >= 8.0.0
136
136
  - **Svelte** 5 (runes mode)
137
137
  - **SvelteKit** (recommended, but not required for basic features)
138
+ - **[@vitejs/devtools](https://devtools.vite.dev/)** — the host UI this plugin's panels render inside
138
139
 
139
140
  ## Installation
140
141
 
142
+ `vite-devtools-svelte` is a panel provider — it needs the [`@vitejs/devtools`](https://devtools.vite.dev/) host plugin to render its UI. Install both:
143
+
141
144
  ```bash
142
- npm install -D vite-devtools-svelte
145
+ npm install -D vite-devtools-svelte @vitejs/devtools
143
146
  ```
144
147
 
145
148
  ## Setup
146
149
 
147
- Add the plugin to your `vite.config.ts`. **It must come before `sveltekit()`** so that the transforms run before the Svelte compiler.
150
+ Register **both** plugins in your `vite.config.ts`. `svelteDevtools()` **must come before `sveltekit()`** so that its transforms run before the Svelte compiler.
148
151
 
149
152
  ```ts
150
153
  // vite.config.ts
151
- import { svelteDevtools } from 'vite-devtools-svelte'
152
154
  import { sveltekit } from '@sveltejs/kit/vite'
155
+ import { DevTools } from '@vitejs/devtools'
156
+ import { svelteDevtools } from 'vite-devtools-svelte'
153
157
  import { defineConfig } from 'vite'
154
158
 
155
159
  export default defineConfig({
156
- plugins: [svelteDevtools(), sveltekit()],
160
+ plugins: [
161
+ svelteDevtools(), // must come before sveltekit()
162
+ DevTools(), // the @vitejs/devtools host — without it the panels have nowhere to render
163
+ sveltekit(),
164
+ ],
157
165
  })
158
166
  ```
159
167
 
@@ -163,7 +171,10 @@ Then start your dev server as usual:
163
171
  npm run dev
164
172
  ```
165
173
 
166
- The Svelte DevTools panels will appear inside the Vite DevTools UI.
174
+ Open your app in the browser. The Vite DevTools drawer appears at the bottom edge of the page — click the floating handle to open it, then switch to the **Svelte** tab to see the panels provided by this plugin.
175
+
176
+ > [!NOTE]
177
+ > If you only register `svelteDevtools()` without `DevTools()` from `@vitejs/devtools`, the dev server starts fine but **no DevTools UI appears** — there is no host for the panels to mount into.
167
178
 
168
179
  ## Options
169
180
 
package/dist/index.mjs CHANGED
@@ -258,8 +258,8 @@ function N(e) {
258
258
  }
259
259
  //#endregion
260
260
  //#region src/runtime.ts
261
- var P = "virtual:svelte-devtools-runtime", F = "\0virtual:svelte-devtools-runtime", I = "\0svelte-devtools:wrapped-client", L = "\nimport * as __svelte_original from 'svelte/internal/client';\nexport * from 'svelte/internal/client';\n\nfunction __dt() {\n return typeof window !== 'undefined' ? window.__SVELTE_DEVTOOLS__ : null;\n}\n\n// Component ID stack (parallel to runtime._stack but local to wrapper)\nconst __idStack = [];\nfunction __currentId() {\n return __idStack.length > 0 ? __idStack[__idStack.length - 1] : null;\n}\n\n// Tracks the most recently created signal for type determination in tag()\nconst __pendingSignal = { ref: null, type: null };\n\n// --- Component Lifecycle ---\n//\n// Observer must not break the observed: every devtools side-effect below is\n// wrapped in try/catch so a bug or an out-of-shape runtime cannot tear down\n// the user's app. The original Svelte function is always called, and its\n// return value always returned, regardless of devtools failures.\n\nexport function push() {\n const result = __svelte_original.push.apply(null, arguments);\n try {\n const dt = __dt();\n if (dt) {\n const file = dt._pendingFile || 'Unknown';\n dt._pendingFile = null;\n const id = dt.register(file);\n __idStack.push(id);\n dt.startInit(id);\n try {\n __svelte_original.user_effect(() => {\n return () => { try { dt.unmount(id); } catch {} };\n });\n } catch {}\n }\n } catch {}\n return result;\n}\n\nexport function pop() {\n // Capture devtools errors but ALWAYS forward to the original pop so the\n // Svelte component stack stays balanced.\n try {\n const dt = __dt();\n if (dt && __idStack.length > 0) {\n const id = __idStack.pop();\n try { dt.endInit(id); } catch {}\n try { dt.registered(id); } catch {}\n }\n } catch {}\n return __svelte_original.pop.apply(null, arguments);\n}\n\n// --- Signal Creation (type markers) ---\n\nexport function state() {\n const signal = __svelte_original.state.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'state';\n } catch {}\n return signal;\n}\n\nexport function derived() {\n const signal = __svelte_original.derived.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'derived';\n } catch {}\n return signal;\n}\n\nexport function proxy() {\n const p = __svelte_original.proxy.apply(null, arguments);\n try {\n __pendingSignal.ref = p;\n __pendingSignal.type = 'proxy';\n } catch {}\n return p;\n}\n\n// --- Signal Tagging (Svelte dev mode) ---\n\nexport function tag(signal, name) {\n const result = __svelte_original.tag.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n const type = (__pendingSignal.ref === signal) ? __pendingSignal.type : 'state';\n if (type === 'derived') {\n dt.trackDerived(signal, name, cid);\n } else {\n dt.trackState(signal, name, cid);\n }\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\nexport function tag_proxy(proxy, name) {\n const result = __svelte_original.tag_proxy.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt.trackProxy(proxy, name, cid);\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\n// --- Effect Tracking ---\n\nexport function user_effect() {\n const result = __svelte_original.user_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n // Track the effect OBJECT (not the callback). The Svelte runtime\n // populates result.deps with the signals this effect depends on,\n // which is what getReactiveGraph() reads to build edges.\n dt.trackEffect(result, 'effect_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n\nexport function user_pre_effect() {\n const result = __svelte_original.user_pre_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n dt.trackEffect(result, 'effect_pre_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n", R = "\nif (typeof window !== 'undefined' && !window.__SVELTE_DEVTOOLS__) {\n const __SVELTE_DT = {\n _nextId: 0,\n _instances: new Map(),\n _stack: [],\n _pendingFile: null,\n _effectCounter: 0,\n _debounceTimer: null,\n _listeners: new Set(),\n\n // Phase 2: Profiling data\n _profiles: new Map(),\n _initStartTimes: new Map(),\n _profileDebounceTimer: null,\n\n // Phase 2: Reactive graph data\n _reactiveNodes: new Map(),\n _reactiveProxies: new Map(),\n\n // Phase 3: State timeline\n _stateTimeline: [],\n _stateSnapshots: new Map(),\n _timelineDebounceTimer: null,\n\n register(file) {\n const id = this._nextId++;\n const parentId = this._stack.length > 0 ? this._stack[this._stack.length - 1] : null;\n const name = file.split('/').pop()?.replace('.svelte', '') || 'Unknown';\n this._instances.set(id, { id, file, name, parentId, mounted: true, children: [] });\n if (parentId !== null) {\n const parent = this._instances.get(parentId);\n if (parent) parent.children.push(id);\n }\n this._stack.push(id);\n this._scheduleUpdate();\n return id;\n },\n\n registered(id) {\n const idx = this._stack.indexOf(id);\n if (idx !== -1) this._stack.splice(idx, 1);\n },\n\n mount(id) {\n const instance = this._instances.get(id);\n if (instance && !instance.mounted) {\n instance.mounted = true;\n this._scheduleUpdate();\n }\n },\n\n unmount(id) {\n const instance = this._instances.get(id);\n if (instance) {\n if (instance.parentId !== null) {\n const parent = this._instances.get(instance.parentId);\n if (parent) {\n parent.children = parent.children.filter(cid => cid !== id);\n }\n }\n this._removeChildren(id);\n this._cleanupReactiveNodes(id);\n this._instances.delete(id);\n }\n this._scheduleUpdate();\n },\n\n _cleanupReactiveNodes(componentId) {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.componentId === componentId) {\n this._reactiveNodes.delete(nodeId);\n }\n }\n },\n\n _removeChildren(parentId) {\n const parent = this._instances.get(parentId);\n if (!parent) return;\n for (const childId of [...parent.children]) {\n this._removeChildren(childId);\n this._cleanupReactiveNodes(childId);\n this._instances.delete(childId);\n }\n },\n\n _scheduleUpdate() {\n if (this._debounceTimer) clearTimeout(this._debounceTimer);\n this._debounceTimer = setTimeout(() => {\n this._sendUpdate();\n }, 100);\n },\n\n _sendUpdate() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:components', { components });\n }\n },\n\n getTree() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n return components;\n },\n\n // --- Phase 2: Render Profiling ---\n\n startInit(id) {\n this._initStartTimes.set(id, performance.now());\n },\n\n endInit(id) {\n const start = this._initStartTimes.get(id);\n if (start === undefined) return;\n const initTime = performance.now() - start;\n this._initStartTimes.delete(id);\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime,\n renderCount: 0,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n this._scheduleProfileUpdate();\n },\n\n recordRender(id) {\n const profile = this._profiles.get(id);\n if (!profile) {\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime: 0,\n renderCount: 1,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n } else {\n profile.renderCount++;\n profile.lastRenderAt = Date.now();\n }\n this._scheduleProfileUpdate();\n },\n\n recordRenderTime(id, duration) {\n const profile = this._profiles.get(id);\n if (profile) {\n profile.totalRenderTime += duration;\n profile.lastRenderTime = duration;\n }\n },\n\n _scheduleProfileUpdate() {\n if (this._profileDebounceTimer) clearTimeout(this._profileDebounceTimer);\n this._profileDebounceTimer = setTimeout(() => {\n this._sendProfileUpdate();\n }, 500);\n },\n\n _sendProfileUpdate() {\n const profiles = Array.from(this._profiles.values());\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:profiles', { profiles });\n }\n },\n\n getProfiles() {\n return Array.from(this._profiles.values());\n },\n\n resetProfiles() {\n this._profiles.clear();\n this._scheduleProfileUpdate();\n },\n\n // --- Phase 2: Reactive Graph Tracking ---\n\n trackState(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackProxy(proxy, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveProxies.set(nodeId, new WeakRef(proxy));\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef({ v: '(proxy)', _isProxy: true }),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackDerived(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'derived', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackEffect(effect, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(effect || { v: undefined, _isEffect: true }),\n meta: { id: nodeId, type: 'effect', name, componentId, componentFile: instance ? instance.file : '' }\n });\n return effect;\n },\n\n getReactiveGraph() {\n const nodes = [];\n const edges = [];\n const signalToId = new Map();\n\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal || !this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n signalToId.set(signal, nodeId);\n const node = { ...entry.meta };\n if (signal._isProxy) {\n const proxyRef = this._reactiveProxies.get(nodeId);\n const proxy = proxyRef?.deref();\n if (proxy) {\n try {\n node.value = Array.isArray(proxy) ? '[' + proxy.length + ']' : '{' + Object.keys(proxy).length + '}';\n } catch { node.value = '(proxy)'; }\n }\n } else if (node.type !== 'effect' && signal.v !== undefined && typeof signal.v !== 'symbol') {\n try {\n const v = signal.v;\n if (typeof v === 'number' || typeof v === 'string' || typeof v === 'boolean' || v === null) {\n node.value = v;\n } else {\n node.value = '(object)';\n }\n } catch { /* ignore */ }\n }\n nodes.push(node);\n }\n\n // Map proxy internal signals to their proxy node ID\n for (const [nodeId, ref] of this._reactiveProxies) {\n const proxy = ref.deref();\n if (!proxy) { this._reactiveProxies.delete(nodeId); continue; }\n const meta = this._reactiveNodes.get(nodeId)?.meta;\n if (!meta || !this._instances.has(meta.componentId)) { this._reactiveProxies.delete(nodeId); continue; }\n }\n\n // Build edges from deps\n const edgeSet = new Set();\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal) continue;\n if (!signal.deps) continue;\n\n const visited = new Set();\n const queue = [...signal.deps];\n while (queue.length > 0) {\n const dep = queue.shift();\n if (!dep || visited.has(dep)) continue;\n visited.add(dep);\n const depId = signalToId.get(dep);\n if (depId) {\n const key = depId + '>' + nodeId;\n if (depId !== nodeId && !edgeSet.has(key)) {\n edgeSet.add(key);\n edges.push({ from: depId, to: nodeId });\n }\n } else if (dep.deps) {\n for (const d of dep.deps) queue.push(d);\n }\n }\n }\n\n return { nodes, edges };\n },\n\n sendReactiveGraph() {\n const graph = this.getReactiveGraph();\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-graph', graph);\n }\n },\n\n // --- Phase 3: State Timeline ---\n\n _pollStateValues() {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.type !== 'state') continue;\n if (!this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n const signal = entry.signal.deref();\n if (!signal || signal.v === undefined) continue;\n try {\n const currentVal = signal.v;\n const prev = this._stateSnapshots.get(nodeId);\n if (prev !== undefined && currentVal === prev) continue;\n const isPrimitive = currentVal === null || typeof currentVal !== 'object';\n let newSnapshot;\n if (isPrimitive) {\n if (prev !== undefined && prev === currentVal) continue;\n newSnapshot = currentVal;\n } else {\n const currStr = JSON.stringify(currentVal);\n const prevStr = this._stateSnapshotStrs?.get(nodeId);\n if (prevStr === currStr) continue;\n newSnapshot = JSON.parse(currStr);\n if (!this._stateSnapshotStrs) this._stateSnapshotStrs = new Map();\n this._stateSnapshotStrs.set(nodeId, currStr);\n }\n if (this._stateTimeline.length >= 500) this._stateTimeline.splice(0, this._stateTimeline.length - 499);\n this._stateTimeline.push({\n id: nodeId,\n name: entry.meta.name,\n componentFile: entry.meta.componentFile,\n oldValue: prev !== undefined ? prev : null,\n newValue: newSnapshot,\n timestamp: Date.now(),\n });\n this._stateSnapshots.set(nodeId, newSnapshot);\n this._scheduleTimelineUpdate();\n } catch { /* ignore non-serializable */ }\n }\n },\n\n _scheduleTimelineUpdate() {\n if (this._timelineDebounceTimer) clearTimeout(this._timelineDebounceTimer);\n this._timelineDebounceTimer = setTimeout(() => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: this._stateTimeline });\n }\n }, 300);\n },\n\n getStateTimeline() {\n return this._stateTimeline;\n },\n\n clearStateTimeline() {\n this._stateTimeline = [];\n this._scheduleTimelineUpdate();\n },\n\n // --- FPS Monitoring ---\n // Uses requestAnimationFrame to measure frame rate with minimal overhead.\n // Only stores timestamps - no allocations per frame beyond a single array push.\n _fpsFrameTimes: [],\n\n _fpsLoop() {\n this._fpsFrameTimes.push(performance.now());\n requestAnimationFrame(() => this._fpsLoop());\n },\n\n _sampleFps() {\n const now = performance.now();\n // Remove frame timestamps older than 1 second\n const cutoff = now - 1000;\n let i = 0;\n while (i < this._fpsFrameTimes.length && this._fpsFrameTimes[i] < cutoff) i++;\n if (i > 0) this._fpsFrameTimes.splice(0, i);\n const fps = this._fpsFrameTimes.length;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:fps', { timestamp: Date.now(), fps });\n }\n }\n };\n\n window.__SVELTE_DEVTOOLS__ = __SVELTE_DT;\n\n // Poll state values for timeline\n setInterval(() => { __SVELTE_DT._pollStateValues(); }, 200);\n\n // FPS monitoring\n requestAnimationFrame(() => __SVELTE_DT._fpsLoop());\n setInterval(() => __SVELTE_DT._sampleFps(), 500);\n\n // Phase 3: Capture runtime errors\n window.addEventListener('error', (event) => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno,\n stack: event.error?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n window.addEventListener('unhandledrejection', (event) => {\n if (import.meta.hot) {\n const reason = event.reason;\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: reason?.message || String(reason),\n stack: reason?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n\n // Listen for reactive graph requests from server\n if (import.meta.hot) {\n import.meta.hot.on('svelte-devtools:request-reactive-graph', () => {\n __SVELTE_DT.sendReactiveGraph();\n });\n import.meta.hot.on('svelte-devtools:request-state-timeline', () => {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: __SVELTE_DT._stateTimeline });\n });\n import.meta.hot.on('svelte-devtools:clear-state-timeline', () => {\n __SVELTE_DT.clearStateTimeline();\n });\n }\n}\n", z = 1, B = 5, V = 1, H = 30;
262
- function U(e) {
261
+ var P = "virtual:svelte-devtools-runtime", ee = "\0virtual:svelte-devtools-runtime", F = "\0svelte-devtools:wrapped-client", I = "\nimport * as __svelte_original from 'svelte/internal/client';\nexport * from 'svelte/internal/client';\n\nfunction __dt() {\n return typeof window !== 'undefined' ? window.__SVELTE_DEVTOOLS__ : null;\n}\n\n// Component ID stack (parallel to runtime._stack but local to wrapper)\nconst __idStack = [];\nfunction __currentId() {\n return __idStack.length > 0 ? __idStack[__idStack.length - 1] : null;\n}\n\n// Tracks the most recently created signal for type determination in tag()\nconst __pendingSignal = { ref: null, type: null };\n\n// --- Component Lifecycle ---\n//\n// Observer must not break the observed: every devtools side-effect below is\n// wrapped in try/catch so a bug or an out-of-shape runtime cannot tear down\n// the user's app. The original Svelte function is always called, and its\n// return value always returned, regardless of devtools failures.\n\nexport function push() {\n const result = __svelte_original.push.apply(null, arguments);\n try {\n const dt = __dt();\n if (dt) {\n const file = dt._pendingFile || 'Unknown';\n dt._pendingFile = null;\n const id = dt.register(file);\n __idStack.push(id);\n dt.startInit(id);\n try {\n __svelte_original.user_effect(() => {\n return () => { try { dt.unmount(id); } catch {} };\n });\n } catch {}\n }\n } catch {}\n return result;\n}\n\nexport function pop() {\n // Capture devtools errors but ALWAYS forward to the original pop so the\n // Svelte component stack stays balanced.\n try {\n const dt = __dt();\n if (dt && __idStack.length > 0) {\n const id = __idStack.pop();\n try { dt.endInit(id); } catch {}\n try { dt.registered(id); } catch {}\n }\n } catch {}\n return __svelte_original.pop.apply(null, arguments);\n}\n\n// --- Signal Creation (type markers) ---\n\nexport function state() {\n const signal = __svelte_original.state.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'state';\n } catch {}\n return signal;\n}\n\nexport function derived() {\n const signal = __svelte_original.derived.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'derived';\n } catch {}\n return signal;\n}\n\nexport function proxy() {\n const p = __svelte_original.proxy.apply(null, arguments);\n try {\n __pendingSignal.ref = p;\n __pendingSignal.type = 'proxy';\n } catch {}\n return p;\n}\n\n// --- Signal Tagging (Svelte dev mode) ---\n\nexport function tag(signal, name) {\n const result = __svelte_original.tag.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n const type = (__pendingSignal.ref === signal) ? __pendingSignal.type : 'state';\n if (type === 'derived') {\n dt.trackDerived(signal, name, cid);\n } else {\n dt.trackState(signal, name, cid);\n }\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\nexport function tag_proxy(proxy, name) {\n const result = __svelte_original.tag_proxy.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt.trackProxy(proxy, name, cid);\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\n// --- Effect Tracking ---\n\nexport function user_effect() {\n const result = __svelte_original.user_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n // Track the effect OBJECT (not the callback). The Svelte runtime\n // populates result.deps with the signals this effect depends on,\n // which is what getReactiveGraph() reads to build edges.\n dt.trackEffect(result, 'effect_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n\nexport function user_pre_effect() {\n const result = __svelte_original.user_pre_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n dt.trackEffect(result, 'effect_pre_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n", L = "\nif (typeof window !== 'undefined' && !window.__SVELTE_DEVTOOLS__) {\n const __SVELTE_DT = {\n _nextId: 0,\n _instances: new Map(),\n _stack: [],\n _pendingFile: null,\n _effectCounter: 0,\n _debounceTimer: null,\n _listeners: new Set(),\n\n // Phase 2: Profiling data\n _profiles: new Map(),\n _initStartTimes: new Map(),\n _profileDebounceTimer: null,\n\n // Phase 2: Reactive graph data\n _reactiveNodes: new Map(),\n _reactiveProxies: new Map(),\n\n // Phase 3: State timeline\n _stateTimeline: [],\n _stateSnapshots: new Map(),\n _timelineDebounceTimer: null,\n\n register(file) {\n const id = this._nextId++;\n const parentId = this._stack.length > 0 ? this._stack[this._stack.length - 1] : null;\n const name = file.split('/').pop()?.replace('.svelte', '') || 'Unknown';\n this._instances.set(id, { id, file, name, parentId, mounted: true, children: [] });\n if (parentId !== null) {\n const parent = this._instances.get(parentId);\n if (parent) parent.children.push(id);\n }\n this._stack.push(id);\n this._scheduleUpdate();\n return id;\n },\n\n registered(id) {\n const idx = this._stack.indexOf(id);\n if (idx !== -1) this._stack.splice(idx, 1);\n },\n\n mount(id) {\n const instance = this._instances.get(id);\n if (instance && !instance.mounted) {\n instance.mounted = true;\n this._scheduleUpdate();\n }\n },\n\n unmount(id) {\n const instance = this._instances.get(id);\n if (instance) {\n if (instance.parentId !== null) {\n const parent = this._instances.get(instance.parentId);\n if (parent) {\n parent.children = parent.children.filter(cid => cid !== id);\n }\n }\n this._removeChildren(id);\n this._cleanupReactiveNodes(id);\n this._instances.delete(id);\n }\n this._scheduleUpdate();\n },\n\n _cleanupReactiveNodes(componentId) {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.componentId === componentId) {\n this._reactiveNodes.delete(nodeId);\n }\n }\n },\n\n _removeChildren(parentId) {\n const parent = this._instances.get(parentId);\n if (!parent) return;\n for (const childId of [...parent.children]) {\n this._removeChildren(childId);\n this._cleanupReactiveNodes(childId);\n this._instances.delete(childId);\n }\n },\n\n _scheduleUpdate() {\n if (this._debounceTimer) clearTimeout(this._debounceTimer);\n this._debounceTimer = setTimeout(() => {\n this._sendUpdate();\n }, 100);\n },\n\n _sendUpdate() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:components', { components });\n }\n },\n\n getTree() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n return components;\n },\n\n // --- Phase 2: Render Profiling ---\n\n startInit(id) {\n this._initStartTimes.set(id, performance.now());\n },\n\n endInit(id) {\n const start = this._initStartTimes.get(id);\n if (start === undefined) return;\n const initTime = performance.now() - start;\n this._initStartTimes.delete(id);\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime,\n renderCount: 0,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n this._scheduleProfileUpdate();\n },\n\n recordRender(id) {\n const profile = this._profiles.get(id);\n if (!profile) {\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime: 0,\n renderCount: 1,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n } else {\n profile.renderCount++;\n profile.lastRenderAt = Date.now();\n }\n this._scheduleProfileUpdate();\n },\n\n recordRenderTime(id, duration) {\n const profile = this._profiles.get(id);\n if (profile) {\n profile.totalRenderTime += duration;\n profile.lastRenderTime = duration;\n }\n },\n\n _scheduleProfileUpdate() {\n if (this._profileDebounceTimer) clearTimeout(this._profileDebounceTimer);\n this._profileDebounceTimer = setTimeout(() => {\n this._sendProfileUpdate();\n }, 500);\n },\n\n _sendProfileUpdate() {\n const profiles = Array.from(this._profiles.values());\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:profiles', { profiles });\n }\n },\n\n getProfiles() {\n return Array.from(this._profiles.values());\n },\n\n resetProfiles() {\n this._profiles.clear();\n this._scheduleProfileUpdate();\n },\n\n // --- Phase 2: Reactive Graph Tracking ---\n\n trackState(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackProxy(proxy, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveProxies.set(nodeId, new WeakRef(proxy));\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef({ v: '(proxy)', _isProxy: true }),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackDerived(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'derived', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackEffect(effect, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(effect || { v: undefined, _isEffect: true }),\n meta: { id: nodeId, type: 'effect', name, componentId, componentFile: instance ? instance.file : '' }\n });\n return effect;\n },\n\n getReactiveGraph() {\n const nodes = [];\n const edges = [];\n const signalToId = new Map();\n\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal || !this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n signalToId.set(signal, nodeId);\n const node = { ...entry.meta };\n if (signal._isProxy) {\n const proxyRef = this._reactiveProxies.get(nodeId);\n const proxy = proxyRef?.deref();\n if (proxy) {\n try {\n node.value = Array.isArray(proxy) ? '[' + proxy.length + ']' : '{' + Object.keys(proxy).length + '}';\n } catch { node.value = '(proxy)'; }\n }\n } else if (node.type !== 'effect' && signal.v !== undefined && typeof signal.v !== 'symbol') {\n try {\n const v = signal.v;\n if (typeof v === 'number' || typeof v === 'string' || typeof v === 'boolean' || v === null) {\n node.value = v;\n } else {\n node.value = '(object)';\n }\n } catch { /* ignore */ }\n }\n nodes.push(node);\n }\n\n // Map proxy internal signals to their proxy node ID\n for (const [nodeId, ref] of this._reactiveProxies) {\n const proxy = ref.deref();\n if (!proxy) { this._reactiveProxies.delete(nodeId); continue; }\n const meta = this._reactiveNodes.get(nodeId)?.meta;\n if (!meta || !this._instances.has(meta.componentId)) { this._reactiveProxies.delete(nodeId); continue; }\n }\n\n // Build edges from deps\n const edgeSet = new Set();\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal) continue;\n if (!signal.deps) continue;\n\n const visited = new Set();\n const queue = [...signal.deps];\n while (queue.length > 0) {\n const dep = queue.shift();\n if (!dep || visited.has(dep)) continue;\n visited.add(dep);\n const depId = signalToId.get(dep);\n if (depId) {\n const key = depId + '>' + nodeId;\n if (depId !== nodeId && !edgeSet.has(key)) {\n edgeSet.add(key);\n edges.push({ from: depId, to: nodeId });\n }\n } else if (dep.deps) {\n for (const d of dep.deps) queue.push(d);\n }\n }\n }\n\n return { nodes, edges };\n },\n\n sendReactiveGraph() {\n const graph = this.getReactiveGraph();\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-graph', graph);\n }\n },\n\n // --- Phase 3: State Timeline ---\n\n _pollStateValues() {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.type !== 'state') continue;\n if (!this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n const signal = entry.signal.deref();\n if (!signal || signal.v === undefined) continue;\n try {\n const currentVal = signal.v;\n const prev = this._stateSnapshots.get(nodeId);\n if (prev !== undefined && currentVal === prev) continue;\n const isPrimitive = currentVal === null || typeof currentVal !== 'object';\n let newSnapshot;\n if (isPrimitive) {\n if (prev !== undefined && prev === currentVal) continue;\n newSnapshot = currentVal;\n } else {\n const currStr = JSON.stringify(currentVal);\n const prevStr = this._stateSnapshotStrs?.get(nodeId);\n if (prevStr === currStr) continue;\n newSnapshot = JSON.parse(currStr);\n if (!this._stateSnapshotStrs) this._stateSnapshotStrs = new Map();\n this._stateSnapshotStrs.set(nodeId, currStr);\n }\n if (this._stateTimeline.length >= 500) this._stateTimeline.splice(0, this._stateTimeline.length - 499);\n this._stateTimeline.push({\n id: nodeId,\n name: entry.meta.name,\n componentFile: entry.meta.componentFile,\n oldValue: prev !== undefined ? prev : null,\n newValue: newSnapshot,\n timestamp: Date.now(),\n });\n this._stateSnapshots.set(nodeId, newSnapshot);\n this._scheduleTimelineUpdate();\n } catch { /* ignore non-serializable */ }\n }\n },\n\n _scheduleTimelineUpdate() {\n if (this._timelineDebounceTimer) clearTimeout(this._timelineDebounceTimer);\n this._timelineDebounceTimer = setTimeout(() => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: this._stateTimeline });\n }\n }, 300);\n },\n\n getStateTimeline() {\n return this._stateTimeline;\n },\n\n clearStateTimeline() {\n this._stateTimeline = [];\n this._scheduleTimelineUpdate();\n },\n\n // --- FPS Monitoring ---\n // Uses requestAnimationFrame to measure frame rate with minimal overhead.\n // Only stores timestamps - no allocations per frame beyond a single array push.\n _fpsFrameTimes: [],\n\n _fpsLoop() {\n this._fpsFrameTimes.push(performance.now());\n requestAnimationFrame(() => this._fpsLoop());\n },\n\n _sampleFps() {\n const now = performance.now();\n // Remove frame timestamps older than 1 second\n const cutoff = now - 1000;\n let i = 0;\n while (i < this._fpsFrameTimes.length && this._fpsFrameTimes[i] < cutoff) i++;\n if (i > 0) this._fpsFrameTimes.splice(0, i);\n const fps = this._fpsFrameTimes.length;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:fps', { timestamp: Date.now(), fps });\n }\n }\n };\n\n window.__SVELTE_DEVTOOLS__ = __SVELTE_DT;\n\n // Poll state values for timeline\n setInterval(() => { __SVELTE_DT._pollStateValues(); }, 200);\n\n // FPS monitoring\n requestAnimationFrame(() => __SVELTE_DT._fpsLoop());\n setInterval(() => __SVELTE_DT._sampleFps(), 500);\n\n // Phase 3: Capture runtime errors\n window.addEventListener('error', (event) => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno,\n stack: event.error?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n window.addEventListener('unhandledrejection', (event) => {\n if (import.meta.hot) {\n const reason = event.reason;\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: reason?.message || String(reason),\n stack: reason?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n\n // Listen for reactive graph requests from server\n if (import.meta.hot) {\n import.meta.hot.on('svelte-devtools:request-reactive-graph', () => {\n __SVELTE_DT.sendReactiveGraph();\n });\n import.meta.hot.on('svelte-devtools:request-state-timeline', () => {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: __SVELTE_DT._stateTimeline });\n });\n import.meta.hot.on('svelte-devtools:clear-state-timeline', () => {\n __SVELTE_DT.clearStateTimeline();\n });\n }\n}\n", R = 1, z = 5, B = 1, V = 30;
262
+ function H(e) {
263
263
  return {
264
264
  renderProfiles: e.getRenderProfiles().map((e) => ({
265
265
  componentId: e.componentId,
@@ -273,10 +273,10 @@ function U(e) {
273
273
  takenAt: Date.now()
274
274
  };
275
275
  }
276
- function W(e, t, n) {
276
+ function U(e, t, n) {
277
277
  return Math.abs(e) < t ? "unchanged" : n ? e < 0 ? "improved" : "regressed" : e > 0 ? "improved" : "regressed";
278
278
  }
279
- var G = class {
279
+ var te = class {
280
280
  sessions = /* @__PURE__ */ new Map();
281
281
  active = null;
282
282
  persistDir;
@@ -291,7 +291,7 @@ var G = class {
291
291
  label: e,
292
292
  startedAt: Date.now(),
293
293
  persist: t,
294
- startSnapshot: U(this.getters),
294
+ startSnapshot: H(this.getters),
295
295
  loadProfiles: [],
296
296
  fpsSamples: []
297
297
  };
@@ -311,7 +311,7 @@ var G = class {
311
311
  if (!this.active) throw Error("No active session");
312
312
  let t = this.sessions.get(this.active);
313
313
  if (!t) throw Error("Active session missing from store");
314
- return t.endedAt = Date.now(), t.endSnapshot = U(this.getters), this.active = null, e === "discard" ? this.sessions.delete(t.id) : (e === "disk" || t.persist) && this.persistToDisk(t), t;
314
+ return t.endedAt = Date.now(), t.endSnapshot = H(this.getters), this.active = null, e === "discard" ? this.sessions.delete(t.id) : (e === "disk" || t.persist) && this.persistToDisk(t), t;
315
315
  }
316
316
  get(e) {
317
317
  return this.sessions.get(e) || this.loadFromDisk(e);
@@ -367,7 +367,7 @@ var G = class {
367
367
  totalRenderTimeDelta: o,
368
368
  avgRenderTimeDelta: a > 0 ? o / a : 0
369
369
  };
370
- }).filter((e) => e.renderCountDelta > 0).sort((e, t) => t.totalRenderTimeDelta - e.totalRenderTimeDelta), i = t.loadProfiles.map((e) => e.duration), a = K(i), o = ee(i, 95), s = t.fpsSamples.map((e) => e.fps), c = K(s), l = s.length ? Math.min(...s) : 0, u = s.filter((e) => e < H).length;
370
+ }).filter((e) => e.renderCountDelta > 0).sort((e, t) => t.totalRenderTimeDelta - e.totalRenderTimeDelta), i = t.loadProfiles.map((e) => e.duration), a = W(i), o = ne(i, 95), s = t.fpsSamples.map((e) => e.fps), c = W(s), l = s.length ? Math.min(...s) : 0, u = s.filter((e) => e < V).length;
371
371
  return {
372
372
  durationMs: t.endedAt - t.startedAt,
373
373
  components: r,
@@ -399,19 +399,19 @@ var G = class {
399
399
  totalRenderTimeDeltaA: o,
400
400
  totalRenderTimeDeltaB: s,
401
401
  diff: c,
402
- verdict: W(c, z, !0)
402
+ verdict: U(c, R, !0)
403
403
  },
404
404
  load: {
405
405
  avgA: n.loadProfiles.avgDuration,
406
406
  avgB: r.loadProfiles.avgDuration,
407
407
  diff: l,
408
- verdict: W(l, B, !0)
408
+ verdict: U(l, z, !0)
409
409
  },
410
410
  fps: {
411
411
  avgA: n.fps.avg,
412
412
  avgB: r.fps.avg,
413
413
  diff: u,
414
- verdict: W(u, V, !1)
414
+ verdict: U(u, B, !1)
415
415
  }
416
416
  };
417
417
  }
@@ -432,26 +432,26 @@ var G = class {
432
432
  }
433
433
  }
434
434
  };
435
- function K(e) {
435
+ function W(e) {
436
436
  return e.length ? e.reduce((e, t) => e + t, 0) / e.length : 0;
437
437
  }
438
- function ee(e, t) {
438
+ function ne(e, t) {
439
439
  if (!e.length) return 0;
440
440
  let n = [...e].sort((e, t) => e - t);
441
441
  return n[Math.min(n.length - 1, Math.floor(t / 100 * n.length))];
442
442
  }
443
443
  //#endregion
444
444
  //#region src/mcp/issues.ts
445
- var q = {
445
+ var G = {
446
446
  avgRenderTimeMs: 4,
447
447
  renderCount: 30,
448
448
  loadDurationMs: 200,
449
449
  fpsDropThreshold: 40,
450
450
  effectMaxDeps: 8
451
451
  };
452
- function te(e, t = {}) {
452
+ function re(e, t = {}) {
453
453
  let n = {
454
- ...q,
454
+ ...G,
455
455
  ...t
456
456
  }, r = [];
457
457
  for (let t of e.renderProfiles) {
@@ -463,9 +463,9 @@ function te(e, t = {}) {
463
463
  summary: `${t.name} averages ${e.toFixed(2)}ms per render (${t.renderCount} renders)`,
464
464
  file: t.file,
465
465
  metric: {
466
- avgRenderTimeMs: Y(e),
466
+ avgRenderTimeMs: q(e),
467
467
  renderCount: t.renderCount,
468
- totalRenderTimeMs: Y(t.totalRenderTime)
468
+ totalRenderTimeMs: q(t.totalRenderTime)
469
469
  },
470
470
  suggestedTool: "get_render_profile"
471
471
  }), t.renderCount >= n.renderCount && r.push({
@@ -476,7 +476,7 @@ function te(e, t = {}) {
476
476
  file: t.file,
477
477
  metric: {
478
478
  renderCount: t.renderCount,
479
- avgRenderTimeMs: Y(e)
479
+ avgRenderTimeMs: q(e)
480
480
  },
481
481
  suggestedTool: "get_component_hotspots"
482
482
  });
@@ -488,7 +488,7 @@ function te(e, t = {}) {
488
488
  summary: `${t.type} load for ${t.route} took ${t.duration.toFixed(0)}ms`,
489
489
  file: t.file,
490
490
  metric: {
491
- durationMs: Y(t.duration),
491
+ durationMs: q(t.duration),
492
492
  dataSizeBytes: t.dataSize
493
493
  },
494
494
  suggestedTool: "get_load_waterfall"
@@ -539,22 +539,22 @@ function te(e, t = {}) {
539
539
  });
540
540
  }
541
541
  }
542
- return r.sort(ne);
542
+ return r.sort(ie);
543
543
  }
544
- var J = {
544
+ var K = {
545
545
  high: 0,
546
546
  medium: 1,
547
547
  low: 2
548
548
  };
549
- function ne(e, t) {
550
- return J[e.severity] - J[t.severity];
549
+ function ie(e, t) {
550
+ return K[e.severity] - K[t.severity];
551
551
  }
552
- function Y(e) {
552
+ function q(e) {
553
553
  return Math.round(e * 100) / 100;
554
554
  }
555
- function re(e, t = {}) {
555
+ function ae(e, t = {}) {
556
556
  let n = {
557
- ...q,
557
+ ...G,
558
558
  ...t
559
559
  }, r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
560
560
  for (let t of e.edges) r.set(t.to, (r.get(t.to) ?? 0) + 1), i.set(t.from, (i.get(t.from) ?? 0) + 1);
@@ -585,11 +585,11 @@ function re(e, t = {}) {
585
585
  }
586
586
  //#endregion
587
587
  //#region src/mcp/server.ts
588
- var X = (e) => ({ content: [{
588
+ var J = (e) => ({ content: [{
589
589
  type: "text",
590
590
  text: typeof e == "string" ? e : JSON.stringify(e, null, 2)
591
591
  }] });
592
- function ie(e) {
592
+ function oe(e) {
593
593
  let t = new c({
594
594
  name: "vite-devtools-svelte",
595
595
  version: "0.1.0"
@@ -605,13 +605,13 @@ function ie(e) {
605
605
  effectMaxDeps: u.number().optional()
606
606
  }
607
607
  }, async (t) => {
608
- let n = t, r = await e.getReactiveGraph(), i = te({
608
+ let n = t, r = await e.getReactiveGraph(), i = re({
609
609
  renderProfiles: e.getRenderProfiles(),
610
610
  reactiveGraph: r,
611
611
  loadProfiles: e.getLoadProfiles(),
612
612
  fpsSamples: e.getFpsSamples()
613
613
  }, n);
614
- return X({
614
+ return J({
615
615
  count: i.length,
616
616
  issues: i
617
617
  });
@@ -619,20 +619,20 @@ function ie(e) {
619
619
  title: "Component render hotspots",
620
620
  description: "Top components by total render time, with render count and per-render average.",
621
621
  inputSchema: { topN: u.number().int().min(1).max(200).optional() }
622
- }, async ({ topN: t = 20 }) => X([...e.getRenderProfiles()].map((e) => ({
622
+ }, async ({ topN: t = 20 }) => J([...e.getRenderProfiles()].map((e) => ({
623
623
  file: e.file,
624
624
  name: e.name,
625
625
  componentId: e.componentId,
626
626
  renderCount: e.renderCount,
627
- totalRenderTimeMs: Z(e.totalRenderTime),
628
- avgRenderTimeMs: Z(e.renderCount > 0 ? e.totalRenderTime / e.renderCount : 0),
629
- lastRenderTimeMs: Z(e.lastRenderTime),
627
+ totalRenderTimeMs: Y(e.totalRenderTime),
628
+ avgRenderTimeMs: Y(e.renderCount > 0 ? e.totalRenderTime / e.renderCount : 0),
629
+ lastRenderTimeMs: Y(e.lastRenderTime),
630
630
  lastRenderAt: e.lastRenderAt
631
631
  })).sort((e, t) => t.totalRenderTimeMs - e.totalRenderTimeMs).slice(0, t))), t.registerTool("get_reactive_graph_problems", {
632
632
  title: "Reactive graph problems",
633
633
  description: "Classified reactive graph issues: over-connected effects, orphan deriveds, isolated nodes. Returns categories instead of the full graph.",
634
634
  inputSchema: { effectMaxDeps: u.number().int().min(1).optional() }
635
- }, async ({ effectMaxDeps: t }) => X(re(await e.getReactiveGraph(), { effectMaxDeps: t }))), t.registerTool("get_load_waterfall", {
635
+ }, async ({ effectMaxDeps: t }) => J(ae(await e.getReactiveGraph(), { effectMaxDeps: t }))), t.registerTool("get_load_waterfall", {
636
636
  title: "SvelteKit load waterfall",
637
637
  description: "Load profiles grouped by route, with timing and data size. Optionally filtered by route.",
638
638
  inputSchema: { route: u.string().optional() }
@@ -644,14 +644,14 @@ function ie(e) {
644
644
  let t = r.get(e.route) ?? [];
645
645
  t.push(e), r.set(e.route, t);
646
646
  }
647
- return X([...r.entries()].map(([e, t]) => {
647
+ return J([...r.entries()].map(([e, t]) => {
648
648
  let n = t.map((e) => e.duration);
649
649
  return {
650
650
  route: e,
651
651
  file: t[0]?.file,
652
652
  count: t.length,
653
- avgDuration: Z(ae(n)),
654
- maxDuration: Z(Math.max(...n)),
653
+ avgDuration: Y(se(n)),
654
+ maxDuration: Y(Math.max(...n)),
655
655
  totalDataBytes: t.reduce((e, t) => e + t.dataSize, 0),
656
656
  samples: t
657
657
  };
@@ -670,7 +670,7 @@ function ie(e) {
670
670
  r = r.filter((t) => t.timestamp >= e);
671
671
  }
672
672
  let i = r.filter((e) => e.fps < t);
673
- return X({
673
+ return J({
674
674
  threshold: t,
675
675
  sampleCount: r.length,
676
676
  dropCount: i.length,
@@ -681,27 +681,27 @@ function ie(e) {
681
681
  title: "Render profile for a specific file",
682
682
  description: "Returns render profile entries matching the given component file (substring match).",
683
683
  inputSchema: { file: u.string() }
684
- }, async ({ file: t }) => X(e.getRenderProfiles().filter((e) => e.file.includes(t)).map((e) => ({
684
+ }, async ({ file: t }) => J(e.getRenderProfiles().filter((e) => e.file.includes(t)).map((e) => ({
685
685
  ...e,
686
- totalRenderTimeMs: Z(e.totalRenderTime),
687
- avgRenderTimeMs: Z(e.renderCount > 0 ? e.totalRenderTime / e.renderCount : 0)
686
+ totalRenderTimeMs: Y(e.totalRenderTime),
687
+ avgRenderTimeMs: Y(e.renderCount > 0 ? e.totalRenderTime / e.renderCount : 0)
688
688
  })))), t.registerTool("get_project_info", {
689
689
  title: "Project info",
690
690
  description: "Package name/version, Svelte / SvelteKit / Vite versions, dependency lists.",
691
691
  inputSchema: {}
692
- }, async () => X(e.getProject())), t.registerTool("get_routes", {
692
+ }, async () => J(e.getProject())), t.registerTool("get_routes", {
693
693
  title: "SvelteKit routes",
694
694
  description: "Static analysis of the SvelteKit routes tree.",
695
695
  inputSchema: {}
696
- }, async () => X(e.getRoutes())), t.registerTool("get_live_components", {
696
+ }, async () => J(e.getRoutes())), t.registerTool("get_live_components", {
697
697
  title: "Currently mounted components",
698
698
  description: "Component instances with file, parent, mounted status as currently mounted in the browser.",
699
699
  inputSchema: {}
700
- }, async () => X(e.getLiveComponents())), t.registerTool("get_component_relations", {
700
+ }, async () => J(e.getLiveComponents())), t.registerTool("get_component_relations", {
701
701
  title: "Component import graph",
702
702
  description: "Static import relations between .svelte components.",
703
703
  inputSchema: {}
704
- }, async () => X(e.getComponentRelations())), t.registerTool("start_session", {
704
+ }, async () => J(e.getComponentRelations())), t.registerTool("start_session", {
705
705
  title: "Start measurement session",
706
706
  description: "Begin capturing metrics under a labelled session. Required before compare_sessions. Set `persist:true` to write to disk on end; otherwise the session lives in memory only.",
707
707
  inputSchema: {
@@ -710,7 +710,7 @@ function ie(e) {
710
710
  }
711
711
  }, async ({ label: t, persist: n = !1 }) => {
712
712
  let r = e.sessions.start(t, n);
713
- return X({
713
+ return J({
714
714
  id: r.id,
715
715
  label: r.label,
716
716
  startedAt: r.startedAt,
@@ -726,7 +726,7 @@ function ie(e) {
726
726
  ]).optional() }
727
727
  }, async ({ keep: t = "memory" }) => {
728
728
  let n = e.sessions.end(t), r = n.endedAt ? e.sessions.delta(n.id) : null;
729
- return X({
729
+ return J({
730
730
  id: n.id,
731
731
  label: n.label,
732
732
  startedAt: n.startedAt,
@@ -741,19 +741,19 @@ function ie(e) {
741
741
  a: u.string(),
742
742
  b: u.string()
743
743
  }
744
- }, async ({ a: t, b: n }) => X(e.sessions.compare(t, n))), t.registerTool("list_sessions", {
744
+ }, async ({ a: t, b: n }) => J(e.sessions.compare(t, n))), t.registerTool("list_sessions", {
745
745
  title: "List sessions",
746
746
  description: "In-memory + on-disk sessions, most recent first.",
747
747
  inputSchema: {}
748
- }, async () => X(e.sessions.list())), t.registerTool("load_session", {
748
+ }, async () => J(e.sessions.list())), t.registerTool("load_session", {
749
749
  title: "Load a session by id",
750
750
  description: "Returns the full session record (including delta if ended) by id.",
751
751
  inputSchema: { id: u.string() }
752
752
  }, async ({ id: t }) => {
753
753
  let n = e.sessions.get(t);
754
- if (!n) return X({ error: `Session not found: ${t}` });
754
+ if (!n) return J({ error: `Session not found: ${t}` });
755
755
  let r = n.endedAt ? e.sessions.delta(t) : null;
756
- return X({
756
+ return J({
757
757
  ...n,
758
758
  delta: r
759
759
  });
@@ -761,14 +761,35 @@ function ie(e) {
761
761
  title: "Delete a session",
762
762
  description: "Removes a session from memory and disk.",
763
763
  inputSchema: { id: u.string() }
764
- }, async ({ id: t }) => X({ deleted: e.sessions.delete(t) })), t;
764
+ }, async ({ id: t }) => J({ deleted: e.sessions.delete(t) })), t;
765
765
  }
766
- function ae(e) {
766
+ function se(e) {
767
767
  return e.length ? e.reduce((e, t) => e + t, 0) / e.length : 0;
768
768
  }
769
- function Z(e) {
769
+ function Y(e) {
770
770
  return Math.round(e * 100) / 100;
771
771
  }
772
+ var ce = "templates: {", X = "/@id/@vitejs/devtools/client/inject", le = `<script type=\\"module\\" src=\\"${X}\\"><\/script>`, Z = "</body>";
773
+ function ue(e) {
774
+ if (!e.includes("templates: {") || !e.includes(Z) || e.includes(X)) return null;
775
+ let t = e.indexOf(ce), n = e.indexOf(Z, t);
776
+ return n === -1 ? null : e.slice(0, n) + le + e.slice(n);
777
+ }
778
+ function de() {
779
+ return {
780
+ name: "vite-devtools-svelte:sveltekit-template-injector",
781
+ enforce: "post",
782
+ apply: (e, t) => t.command === "serve" && !t.isSsrBuild,
783
+ transform(e, t) {
784
+ if (!t.endsWith(".svelte-kit/generated/server/internal.js")) return;
785
+ let n = ue(e);
786
+ if (n !== null) return {
787
+ code: n,
788
+ map: null
789
+ };
790
+ }
791
+ };
792
+ }
772
793
  //#endregion
773
794
  //#region src/plugin.ts
774
795
  var Q = e.dirname(i(import.meta.url));
@@ -783,17 +804,17 @@ function $(e) {
783
804
  let r = t.hostname;
784
805
  if (r === "::1" || r === "[::1]") throw Error("Blocked: loopback address");
785
806
  if (n.isIP(r)) {
786
- if (oe(r)) throw Error(`Blocked: private IP address ${r}`);
807
+ if (fe(r)) throw Error(`Blocked: private IP address ${r}`);
787
808
  } else {
788
809
  let e = r.toLowerCase();
789
810
  if (e === "localhost" || e.endsWith(".local") || e.endsWith(".internal")) throw Error(`Blocked: internal hostname ${r}`);
790
811
  }
791
812
  }
792
- function oe(e) {
813
+ function fe(e) {
793
814
  let t = e.split(".").map(Number);
794
815
  return !!(t.length === 4 && (t[0] === 127 || t[0] === 10 || t[0] === 172 && t[1] >= 16 && t[1] <= 31 || t[0] === 192 && t[1] === 168 || t[0] === 169 && t[1] === 254 || t.every((e) => e === 0)));
795
816
  }
796
- function se(n = {}) {
817
+ function pe(n = {}) {
797
818
  let { componentTracking: i = !0 } = n, o, s, c, u = r.randomUUID();
798
819
  function f(n) {
799
820
  if (typeof n != "string" || n.length === 0) throw Error("Invalid file path");
@@ -811,7 +832,7 @@ function se(n = {}) {
811
832
  edges: []
812
833
  }, y = [], b = [], x = [], S = [], w = [], T = [], E = null;
813
834
  function D() {
814
- return E ||= new G({
835
+ return E ||= new te({
815
836
  persistDir: e.join(c, "node_modules", ".vite-devtools-svelte", "sessions"),
816
837
  getters: {
817
838
  getRenderProfiles: () => m,
@@ -985,7 +1006,7 @@ function se(n = {}) {
985
1006
  },
986
1007
  "svelte-devtools:get-module-graph": async () => M(),
987
1008
  "svelte-devtools:get-og-preview": async (e) => N(String(e)),
988
- "svelte-devtools:get-build-analysis": async () => z(),
1009
+ "svelte-devtools:get-build-analysis": async () => R(),
989
1010
  "svelte-devtools:get-fps": async () => T,
990
1011
  "svelte-devtools:clear-fps": async () => {
991
1012
  T = [];
@@ -1119,7 +1140,7 @@ function se(n = {}) {
1119
1140
  r.statusCode = 404, r.end("Not found");
1120
1141
  }
1121
1142
  });
1122
- let S = () => ie({
1143
+ let S = () => oe({
1123
1144
  getProject: () => C(c),
1124
1145
  getRoutes: () => d(C(c).routesDir),
1125
1146
  getLiveComponents: () => p,
@@ -1161,14 +1182,14 @@ function se(n = {}) {
1161
1182
  },
1162
1183
  resolveId(e, t) {
1163
1184
  if (o?.command === "serve") {
1164
- if (e === "virtual:svelte-devtools-runtime") return F;
1165
- if (i && e === "svelte/internal/client" && t && !t.includes("node_modules") && !t.startsWith("\0")) return I;
1185
+ if (e === "virtual:svelte-devtools-runtime") return ee;
1186
+ if (i && e === "svelte/internal/client" && t && !t.includes("node_modules") && !t.startsWith("\0")) return F;
1166
1187
  }
1167
1188
  },
1168
1189
  load(e) {
1169
1190
  if (o?.command === "serve") {
1170
- if (e === "\0virtual:svelte-devtools-runtime") return R;
1171
- if (e === "\0svelte-devtools:wrapped-client") return L;
1191
+ if (e === "\0virtual:svelte-devtools-runtime") return L;
1192
+ if (e === "\0svelte-devtools:wrapped-client") return I;
1172
1193
  }
1173
1194
  },
1174
1195
  transformIndexHtml() {
@@ -1298,7 +1319,7 @@ function se(n = {}) {
1298
1319
  }
1299
1320
  return t;
1300
1321
  }
1301
- function z() {
1322
+ function R() {
1302
1323
  let n = e.resolve(c, ".svelte-kit/output"), r = e.resolve(c, "build/client"), i = [];
1303
1324
  for (let a of [
1304
1325
  n,
@@ -1413,10 +1434,11 @@ export const load = async (event) => {
1413
1434
  t(e, n);
1414
1435
  };
1415
1436
  }
1416
- }
1437
+ },
1438
+ de()
1417
1439
  ];
1418
1440
  }
1419
1441
  //#endregion
1420
- export { se as svelteDevtools };
1442
+ export { pe as svelteDevtools };
1421
1443
 
1422
1444
  //# sourceMappingURL=index.mjs.map