vite-devtools-svelte 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +2 -2
  2. package/dist/client/assets/{ApiPlayground-BMx5j-7t.js → ApiPlayground-B28mCUlr.js} +1 -1
  3. package/dist/client/assets/{Assets-BHhg5Smm.js → Assets-BVT22xZ3.js} +1 -1
  4. package/dist/client/assets/{Badge-CBVv8IFU.js → Badge-x4Lbd8pc.js} +1 -1
  5. package/dist/client/assets/{BuildAnalysis-BdVCnWpb.js → BuildAnalysis-BscZFEWI.js} +1 -1
  6. package/dist/client/assets/{CaptureNotice-BeXCVhO0.js → CaptureNotice-D0u6WNeR.js} +1 -1
  7. package/dist/client/assets/{Components-D_qDgEUt.js → Components-sw5zdu0k.js} +1 -1
  8. package/dist/client/assets/{DataTable-C6-tmcTQ.js → DataTable-CdM2Vyvn.js} +1 -1
  9. package/dist/client/assets/{ErrorDashboard-CfUhPzue.js → ErrorDashboard-BwGMVW_-.js} +1 -1
  10. package/dist/client/assets/{FpsMonitor-B_AuHgyu.js → FpsMonitor-C0FeLiag.js} +1 -1
  11. package/dist/client/assets/{Inspect-BOgmKm6W.js → Inspect-B-jb8MaB.js} +1 -1
  12. package/dist/client/assets/{Inspector-DGmAzhki.js → Inspector-C4wr2Zb4.js} +1 -1
  13. package/dist/client/assets/{LoadProfiler-5xhnDLjR.js → LoadProfiler-WeP1orWN.js} +1 -1
  14. package/dist/client/assets/{ModuleGraph-Cp7f0Nz0.js → ModuleGraph-BYer7OEL.js} +1 -1
  15. package/dist/client/assets/{OGPreview-CjBhXSgn.js → OGPreview-MRxnmCmb.js} +1 -1
  16. package/dist/client/assets/{Overview-Kbpkgsas.js → Overview-CDBKKylY.js} +1 -1
  17. package/dist/client/assets/Overview-Neh721Uf.css +1 -0
  18. package/dist/client/assets/ReactiveGraph-Bni33DP4.js +5 -0
  19. package/dist/client/assets/{RenderProfiler-CeF8_j3b.js → RenderProfiler-2rA8kCUf.js} +1 -1
  20. package/dist/client/assets/{Routes-DArWZtzC.js → Routes-BZ5XcmEt.js} +1 -1
  21. package/dist/client/assets/{SearchField-eocQu1eB.js → SearchField-D85HSGV_.js} +1 -1
  22. package/dist/client/assets/{Segmented-JGNuI5EZ.js → Segmented-BCcIUjA2.js} +1 -1
  23. package/dist/client/assets/{SplitView-B8lYXXEm.js → SplitView-DVk1HAf9.js} +1 -1
  24. package/dist/client/assets/{StateTimeline-HpyvgoK1.js → StateTimeline-DjTXe54T.js} +1 -1
  25. package/dist/client/assets/{TreeView-fty5aCC5.js → TreeView-e7dNH8HW.js} +1 -1
  26. package/dist/client/assets/{VirtualList-BvOBTnYJ.js → VirtualList-Ba0GhFn8.js} +1 -1
  27. package/dist/client/assets/{browser-agent-rpc-BXhoSh1z-D9g7mT9G.js → browser-agent-rpc-BXhoSh1z-UMmvacmP.js} +1 -1
  28. package/dist/client/assets/{capture.svelte-CUhwdJwX.js → capture.svelte-fve1ihQA.js} +1 -1
  29. package/dist/client/assets/{index-BzMw8R8l.js → index-CuoAAt-d.js} +3 -3
  30. package/dist/client/assets/{index-eZHqMk0z.css → index-u73uo_Ri.css} +1 -1
  31. package/dist/client/assets/{reactive-C2hv2QLH.js → reactive-BSjTkgqc.js} +1 -1
  32. package/dist/client/assets/reactive-selection.svelte-BmjgYyWR.js +1 -0
  33. package/dist/client/assets/{versions-Co596WUJ.css → versions-CMau2xlU.css} +1 -1
  34. package/dist/client/assets/{versions-CZjRj40E.js → versions-zyEGB_mz.js} +1 -1
  35. package/dist/client/index.html +2 -2
  36. package/dist/index.mjs +90 -91
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/mcp/issues.d.ts +10 -1
  39. package/dist/plugin.d.ts +22 -0
  40. package/dist/runtime.d.ts +2 -2
  41. package/dist/types.d.ts +26 -2
  42. package/package.json +4 -1
  43. package/dist/client/assets/Overview-B8hvIsIM.css +0 -1
  44. package/dist/client/assets/ReactiveGraph-w2TK82c8.js +0 -5
  45. package/dist/client/assets/reactive-selection.svelte-Dfd9zWsh.js +0 -1
package/dist/index.mjs CHANGED
@@ -210,7 +210,7 @@ function he(e) {
210
210
  }
211
211
  //#endregion
212
212
  //#region src/runtime.ts
213
- var S = "virtual:svelte-devtools-runtime", ge = "\0virtual:svelte-devtools-runtime", _e = "\0svelte-devtools:wrapped-client", ve = "\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 context -> devtools id (review B1b). A component created later\n// by a block update ({#each}/{#if} after mount) runs outside its parent's\n// push..pop, where the runtime's init stack is empty (the wrapper stack\n// below covers the wrapped block helpers; this covers the rest). Svelte (5.56.8,\n// internal/client) exports active_effect; every effect records the\n// component context it was created in (effect.ctx) and runs with that\n// context restored. The block effect that creates the child belongs to the\n// parent's template, so active_effect.ctx at push time IS the parent's\n// context. Each context is mapped to its id once the component mounted\n// (inside its own deferred effect, where active_effect.ctx is the\n// component's own context). Weak map: no leak, no Svelte state touched.\nconst __idByContext = new WeakMap();\nfunction __parentFromContext() {\n const effect = __svelte_original.active_effect;\n const ctx = effect ? effect.ctx : null;\n return ctx ? __idByContext.get(ctx) : undefined;\n}\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 // Parent hint, read before this component's own id exists:\n // 1. the wrapper stack: the component whose init is running (children\n // rendered directly by a template, where active_effect still belongs to\n // an outer block), or the block owner re-pushed by __wrapBlockFn for a\n // later {#each}/{#if}/{#key} update (see Block Attribution below);\n // 2. else the context-derived owner, for re-creation paths the wrapper\n // does not wrap (snippet, svelte:element, ...).\n let parentHint = null;\n try { parentHint = __currentId(); } catch {}\n if (parentHint === null) {\n try { parentHint = __parentFromContext(); } catch {}\n }\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 dt._pendingParent = parentHint;\n const id = dt.register(file);\n __idStack.push(id);\n dt.startInit(id);\n try {\n __svelte_original.user_effect(() => {\n try {\n const own = __svelte_original.active_effect;\n if (own && own.ctx) __idByContext.set(own.ctx, id);\n } catch {}\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\n// --- Render Profiling ---\n//\n// Svelte 5 has no virtual DOM: the compiler wraps every dynamic part of a\n// template in a template_effect (deferred_template_effect for <svelte:head>\n// <title>). A re-run of such an effect IS the component updating the DOM, so\n// these two functions are the measurement points for renders / render time.\n//\n// The first run of an effect is the mount-time paint, already covered by\n// initTime, so it is skipped (initialRun). One state change re-runs several\n// effects of the same component, so durations are pooled per microtask and\n// recorded once per flush: recordRender() +1, recordRenderTime() the sum.\n\nconst __pendingRenderDurations = new Map();\nlet __renderFlushScheduled = false;\n\nfunction __flushRenderDurations() {\n __renderFlushScheduled = false;\n const dt = __dt();\n for (const [cid, duration] of __pendingRenderDurations) {\n if (dt) {\n try { dt.recordRender(cid); } catch {}\n try { dt.recordRenderTime(cid, duration); } catch {}\n }\n }\n __pendingRenderDurations.clear();\n}\n\nfunction __recordRenderDuration(cid, duration) {\n __pendingRenderDurations.set(cid, (__pendingRenderDurations.get(cid) || 0) + duration);\n if (!__renderFlushScheduled) {\n __renderFlushScheduled = true;\n queueMicrotask(__flushRenderDurations);\n }\n}\n\n// Returns a (possibly new) args array whose first element times each re-run\n// of the effect closure. The owning component is whatever id is on top of\n// __idStack at effect-creation time. User exceptions propagate untouched;\n// only the args are built inside try so the original is called exactly once.\nfunction __wrapTemplateEffect(args) {\n const cid = __currentId();\n const fn = args[0];\n if (cid === null || typeof fn !== 'function') return args;\n const wrapped = Array.prototype.slice.call(args);\n let initialRun = true;\n wrapped[0] = function () {\n if (initialRun) {\n initialRun = false;\n return fn.apply(this, arguments);\n }\n const start = performance.now();\n try {\n return fn.apply(this, arguments);\n } finally {\n try { __recordRenderDuration(cid, performance.now() - start); } catch {}\n }\n };\n return wrapped;\n}\n\nexport function template_effect() {\n let args = arguments;\n try { args = __wrapTemplateEffect(arguments); } catch {}\n return __svelte_original.template_effect.apply(null, args);\n}\n\nexport function deferred_template_effect() {\n let args = arguments;\n try { args = __wrapTemplateEffect(arguments); } catch {}\n return __svelte_original.deferred_template_effect.apply(null, args);\n}\n\n// --- Block Attribution ---\n//\n// Svelte creates render effects synchronously at creation time. At mount this\n// happens during the component function (between push/pop), so __idStack has\n// the owner. But effects for {#each} items added later or re-created {#if}\n// branches are created during a batch flush, when the stack is empty — they\n// would never be attributed. Block helpers themselves ARE always called\n// during their owner's init (or inside an outer block's callback), so the\n// owner id is on the stack at block-creation time. __wrapBlock captures it\n// and re-pushes it around every function argument the block invokes later,\n// so effects created inside land on the innermost owner. Child components\n// push their own id on top, keeping nested attribution correct.\n\nfunction __wrapBlockFn(fn, cid) {\n return function () {\n let pushed = false;\n try { __idStack.push(cid); pushed = true; } catch {}\n try {\n return fn.apply(this, arguments);\n } finally {\n if (pushed) { try { __idStack.pop(); } catch {} }\n }\n };\n}\n\nfunction __wrapBlock(args) {\n const cid = __currentId();\n if (cid === null) return args;\n const wrapped = Array.prototype.slice.call(args);\n for (let i = 0; i < wrapped.length; i++) {\n if (typeof wrapped[i] === 'function') wrapped[i] = __wrapBlockFn(wrapped[i], cid);\n }\n return wrapped;\n}\n\nexport function each() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.each.apply(null, args);\n}\n\nexport function key() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.key.apply(null, args);\n}\n\nexport function component() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.component.apply(null, args);\n}\n\nexport function boundary() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.boundary.apply(null, args);\n}\n\n// 'if' and 'await' are reserved words, so they cannot be declared as\n// function names and are exported via aliases instead.\nfunction __if_block() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original['if'].apply(null, args);\n}\n\nfunction __await_block() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original['await'].apply(null, args);\n}\n\nexport { __if_block as if, __await_block as await };\n", ye = "\nif (typeof window !== 'undefined' && !window.__SVELTE_DEVTOOLS__) {\n const __NO_VALUE = Symbol('no value');\n // State timeline ring (§6.7 C): every read sees at most 500 entries and\n // __TIMELINE_BYTES (also the server's buffer budget). The raw array may\n // hold up to __TIMELINE_TRIM_AT (2 x the 500 cap) between bulk trims.\n const __TIMELINE_BYTES = 4 * 1024 * 1024;\n const __TIMELINE_TRIM_AT = 1000;\n // FNV-1a over UTF-16 code units: cheap change detection for large snapshots.\n const __hash = (str) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(36);\n };\n const __SVELTE_DT = {\n _nextId: 0,\n _instances: new Map(),\n _stack: [],\n _pendingParent: undefined,\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 // componentId -> Set<nodeId>. Lets unmount purge a component's reactive\n // nodes without scanning every node of the app (was O(components x nodes)).\n _nodesByComponent: new Map(),\n\n // Phase 3: State timeline\n _stateTimeline: [],\n _stateSnapshots: new Map(),\n _timelineDebounceTimer: null,\n // signal (or proxy marker) -> nodeId, set at track time and removed by\n // _forgetNode: a scoped graph resolves dependencies without scanning\n // every node (§6.7 D).\n _idBySignal: new WeakMap(),\n // Polling bookkeeping (§6.7 D): $state node ids in a dense array kept up\n // to date incrementally (_pollIndex: nodeId -> position; removal is\n // swap-with-last), a cursor carried across ticks, nodes found changed but\n // not yet serialized (_pollDirty), and per-node change hints\n // (nodeId -> { ref, wv, nextCheckAt }).\n _pollIds: [],\n _pollIndex: new Map(),\n _pollCursor: 0,\n _pollDirty: new Set(),\n // Full-sweep bookkeeping: how long the last complete pass over all $state\n // nodes took (ms), for disclosure (= the worst-case detection delay).\n _pollSweep: { startedAt: 0, lastMs: null, nodes: 0 },\n _pollMeta: new Map(),\n _deepCredit: 0,\n _lastSnapshotSize: new Map(),\n _timelineSeq: 0,\n _pushedSeq: 0,\n _timelineReset: false,\n _entryInfo: new WeakMap(),\n // Sum of _entryInfo.bytes over _stateTimeline (runtime byte budget).\n _timelineBytes: 0,\n // Disclosure since the last push (§6.7 C): unsent entries the ring removed\n // (by count / by bytes) and changes recorded only as a size summary.\n _timelineDropped: { 'runtime-count': 0, 'runtime-bytes': 0 },\n _valueTooLarge: 0,\n\n // Overview aggregate (§6.7 I/J), O(1) per event: registered nodes per\n // component and type (+ app totals), and per-component activity in a\n // ring of 60 one-second buckets (sampled changes, renders, render ms,\n // plus how long sampling ran in that second).\n _nodeCounts: new Map(),\n _nodeTotals: { state: 0, derived: 0, effect: 0 },\n _activity: [],\n _epoch: Math.random().toString(36).slice(2) + Date.now().toString(36),\n\n // --- Activity subscription (docs/devframe-migration.md §6.3) ---\n // Inactive until the server says a DevTools client (UI tab or MCP lease)\n // is watching: no state polling, FPS rAF loop, or periodic HMR sends.\n _active: false,\n _componentsCost: { ms: 0, bytes: 0 },\n _componentDeltas: false,\n _pendingAdded: new Set(),\n _pendingRemoved: new Set(),\n _lastFullAt: 0,\n _resync: false,\n _profilesCost: { ms: 0, bytes: 0 },\n // = collector LIMITS.renderProfiles (pinned by a unit test): the server\n // keeps only the newest entries, so sending more is wasted bytes.\n _profileCap: 5000,\n _sampling: null,\n _baselining: false,\n // Polled state nodes not observed yet (no seed value). Their first\n // observation is a seed, not a change: no timeline entry, no activity\n // count, no drops — the old value is unknown, not null (review B-1/B-2).\n _unseeded: new Set(),\n // pendingNodes last sent on a state-timeline message (null = none yet)\n _sentPendingNodes: null,\n _fpsGen: 0,\n\n register(file) {\n const id = this._nextId++;\n // Parent: the wrapper's context-derived hint (components created by a\n // later block update, review B1b) when it names a live instance, else\n // the synchronous init stack (children created during the parent's own\n // init, before the parent is mapped).\n const hint = this._pendingParent;\n this._pendingParent = undefined;\n const parentId =\n hint !== undefined && hint !== null && this._instances.has(hint)\n ? hint\n : this._stack.length > 0 ? this._stack[this._stack.length - 1] : null;\n const name = file.split('/').pop()?.replace('.svelte', '') || 'Unknown';\n // children is a Set so unmounting one row of a 5 000-row list is O(1).\n this._instances.set(id, { id, file, name, parentId, mounted: true, children: new Set() });\n if (parentId !== null) {\n const parent = this._instances.get(parentId);\n if (parent) parent.children.add(id);\n }\n this._stack.push(id);\n if (this._active) this._pendingAdded.add(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 if (this._active) this._pendingAdded.add(id);\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) parent.children.delete(id);\n }\n this._removeChildren(id);\n this._cleanupComponent(id);\n }\n this._scheduleUpdate();\n this._scheduleProfileUpdate();\n },\n\n // Remounts get a fresh id, so entries keyed by a dead id would otherwise\n // survive forever — every per-component map must be purged here.\n _cleanupComponent(id) {\n // Delta bookkeeping: an id added and removed between two pushes was\n // never sent, so it cancels out.\n if (this._active && !this._pendingAdded.delete(id)) this._pendingRemoved.add(id);\n this._cleanupReactiveNodes(id);\n this._instances.delete(id);\n this._profiles.delete(id);\n this._initStartTimes.delete(id);\n },\n\n _cleanupReactiveNodes(componentId) {\n const nodeIds = this._nodesByComponent.get(componentId);\n if (!nodeIds) return;\n this._nodesByComponent.delete(componentId);\n for (const nodeId of nodeIds) this._forgetNode(nodeId);\n },\n\n _forgetNode(nodeId) {\n const entry = this._reactiveNodes.get(nodeId);\n if (entry) this._countNode(entry.meta.componentId, entry.meta.type, -1);\n const signal = entry && entry.signal.deref();\n if (signal && this._idBySignal.get(signal) === nodeId) this._idBySignal.delete(signal);\n this._pollRemove(nodeId);\n this._pollDirty.delete(nodeId);\n this._reactiveNodes.delete(nodeId);\n this._reactiveProxies.delete(nodeId);\n this._stateSnapshots.delete(nodeId);\n if (this._stateSnapshotStrs) this._stateSnapshotStrs.delete(nodeId);\n this._pollMeta.delete(nodeId);\n this._lastSnapshotSize.delete(nodeId);\n },\n\n _indexNode(nodeId, componentId) {\n let set = this._nodesByComponent.get(componentId);\n if (!set) {\n set = new Set();\n this._nodesByComponent.set(componentId, set);\n }\n set.add(nodeId);\n },\n\n _pollAdd(nodeId) {\n if (this._pollIndex.has(nodeId)) return;\n if (!this._stateSnapshots.has(nodeId)) this._unseeded.add(nodeId);\n this._pollIndex.set(nodeId, this._pollIds.length);\n this._pollIds.push(nodeId);\n },\n\n // O(1): the last id takes the removed slot. If that slot is behind the\n // cursor, the moved id is visited in the next sweep instead of this one\n // (still visited within a bounded number of ticks).\n // Before a track* call replaces/creates nodeId: move the count from the\n // previous registration (same name re-tracked) to the new one.\n _trackNodeCount(nodeId, componentId, type) {\n const prev = this._reactiveNodes.get(nodeId);\n if (prev) this._countNode(prev.meta.componentId, prev.meta.type, -1);\n this._countNode(componentId, type, 1);\n },\n\n _countNode(componentId, type, d) {\n const kind = type === 'derived' || type === 'effect' ? type : 'state';\n let c = this._nodeCounts.get(componentId);\n if (!c) {\n if (d < 0) return;\n c = { state: 0, derived: 0, effect: 0 };\n this._nodeCounts.set(componentId, c);\n }\n c[kind] += d;\n this._nodeTotals[kind] += d;\n if (c.state + c.derived + c.effect <= 0) this._nodeCounts.delete(componentId);\n },\n\n _bucket() {\n const sec = Math.floor(performance.now() / 1000);\n const slot = ((sec % 60) + 60) % 60;\n let b = this._activity[slot];\n if (!b || b.second !== sec) {\n b = { second: sec, sampledMs: 0, rows: new Map() };\n this._activity[slot] = b;\n }\n return b;\n },\n\n _activityRow(componentId) {\n const rows = this._bucket().rows;\n let r = rows.get(componentId);\n if (!r) {\n r = { changes: 0, renders: 0, renderMs: 0 };\n rows.set(componentId, r);\n }\n return r;\n },\n\n _pollRemove(nodeId) {\n const i = this._pollIndex.get(nodeId);\n if (i === undefined) return;\n this._unseeded.delete(nodeId);\n const ids = this._pollIds;\n const last = ids.pop();\n this._pollIndex.delete(nodeId);\n if (i < ids.length) {\n ids[i] = last;\n this._pollIndex.set(last, i);\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._cleanupComponent(childId);\n }\n },\n\n // Full-list pushes are throttled, not debounced: a pending timer is not\n // reset, so continuous churn still produces updates (a debounce would\n // starve), and the interval adapts to the last push's cost so a 10 000+\n // instance app can't spend its main thread serializing (review P3):\n // max(min, 20 x last send ms, 1 s when the last payload exceeded ~1 MB).\n _sendInterval(cost, min) {\n return Math.max(min, 20 * cost.ms, cost.bytes > 1000000 ? 1000 : 0);\n },\n\n _scheduleUpdate() {\n if (!this._active || this._debounceTimer) return;\n this._debounceTimer = setTimeout(() => {\n this._debounceTimer = null;\n this._sendUpdate();\n }, this._sendInterval(this._componentsCost, 100));\n },\n\n // components push (§6.5). Full form { epoch, components } replaces the\n // server's tree for this epoch: sent on activation, when the server does\n // not advertise componentDeltas, as a periodic checkpoint, or when the\n // delta would be larger than the tree. Otherwise the delta form\n // { epoch, added, removed } (removed ids first, ≤ 2 000 entries per\n // message, added in registration order = parents first).\n _sendUpdate(forceFull) {\n // Nobody watching: keep the registry, skip the O(N) payload. A full\n // snapshot is sent on activation (§6.3).\n if (!this._active) return;\n const t0 = performance.now();\n const CHUNK = 2000;\n const CHECKPOINT_MS = 30000;\n const pending = this._pendingAdded.size + this._pendingRemoved.size;\n const full =\n forceFull ||\n !this._componentDeltas ||\n t0 - this._lastFullAt >= CHECKPOINT_MS ||\n pending > this._instances.size;\n const toEntry = (instance) => ({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n let bytes = 0;\n const msgs = [];\n if (full) {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push(toEntry(instance));\n bytes += instance.file.length + instance.name.length + 72;\n }\n }\n msgs.push({ epoch: this._epoch, reset: true, components });\n this._lastFullAt = t0;\n } else if (pending > 0) {\n const removed = [...this._pendingRemoved];\n const added = [];\n for (const id of this._pendingAdded) {\n const instance = this._instances.get(id);\n if (instance && instance.mounted) {\n added.push(toEntry(instance));\n bytes += instance.file.length + instance.name.length + 72;\n }\n }\n bytes += removed.length * 8;\n for (let r = 0; r < removed.length; r += CHUNK) {\n msgs.push({ epoch: this._epoch, added: [], removed: removed.slice(r, r + CHUNK) });\n }\n for (let a = 0; a < added.length; a += CHUNK) {\n msgs.push({ epoch: this._epoch, added: added.slice(a, a + CHUNK), removed: [] });\n }\n }\n this._pendingAdded.clear();\n this._pendingRemoved.clear();\n if (import.meta.hot) {\n for (const msg of msgs) import.meta.hot.send('svelte-devtools:components', msg);\n }\n this._componentsCost = { ms: performance.now() - t0, bytes };\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._activityRow(id).renders++;\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 this._activityRow(id).renderMs += duration;\n }\n },\n\n // Profiles push (review P-1 Option A; contract in\n // docs/performance-status.md). Throttled like components, but the floor\n // grows with the payload: every push carries the whole retained map, so\n // a large app coalesces bursts instead of resending ~1 MB twice a second.\n // Hard cap 2 s (contract R6): a slow serialization must not stretch the\n // latency beyond it.\n _profileInterval() {\n const cost = this._profilesCost;\n return Math.min(\n 2000,\n Math.max(500, 20 * cost.ms, cost.bytes > 1000000 ? 2000 : cost.bytes > 250000 ? 1000 : 0),\n );\n },\n\n _scheduleProfileUpdate() {\n if (!this._active || this._profileDebounceTimer) return;\n this._profileDebounceTimer = setTimeout(() => {\n this._profileDebounceTimer = null;\n this._sendProfileUpdate();\n }, this._profileInterval());\n },\n\n // Also the forced path (activation / resync): a pending throttled push is\n // cancelled so the same state is not sent twice.\n _sendProfileUpdate() {\n if (this._profileDebounceTimer) {\n clearTimeout(this._profileDebounceTimer);\n this._profileDebounceTimer = null;\n }\n if (!this._active) return;\n const t0 = performance.now();\n const all = Array.from(this._profiles.values());\n // Exactly what the collector retains: its tail, in the same order.\n const profiles = all.length > this._profileCap ? all.slice(all.length - this._profileCap) : all;\n if (import.meta.hot) {\n // total = all retained profiles; the message carries the newest-mounted\n // tail (§6.7 J IA1 capture policy 'newest-mounted').\n import.meta.hot.send('svelte-devtools:profiles', { epoch: this._epoch, profiles, total: all.length });\n }\n // Payload size from a small sample (entries differ mainly by file path).\n const k = Math.min(8, profiles.length);\n let sampled = 0;\n for (let i = 0; i < k; i++) sampled += JSON.stringify(profiles[i]).length;\n const bytes = k > 0 ? Math.round((sampled / k) * profiles.length) : 0;\n this._profilesCost = { ms: performance.now() - t0, bytes };\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._trackNodeCount(nodeId, componentId, 'state');\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n this._idBySignal.set(signal, nodeId);\n this._indexNode(nodeId, componentId);\n this._pollAdd(nodeId);\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 // The proxy itself stays weakly held (_reactiveProxies). The marker is\n // held strongly so the node is not GC-dropped while the component lives;\n // _cleanupComponent removes it on unmount.\n const marker = { v: '(proxy)', _isProxy: true };\n this._trackNodeCount(nodeId, componentId, 'state');\n this._reactiveNodes.set(nodeId, {\n signal: { deref: () => marker },\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n this._idBySignal.set(marker, nodeId);\n this._indexNode(nodeId, componentId);\n this._pollAdd(nodeId);\n },\n\n trackDerived(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._trackNodeCount(nodeId, componentId, 'derived');\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'derived', name, componentId, componentFile: instance ? instance.file : '' }\n });\n this._idBySignal.set(signal, nodeId);\n this._indexNode(nodeId, componentId);\n },\n\n trackEffect(effect, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n const target = effect || { v: undefined, _isEffect: true };\n this._trackNodeCount(nodeId, componentId, 'effect');\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(target),\n meta: { id: nodeId, type: 'effect', name, componentId, componentFile: instance ? instance.file : '' }\n });\n this._idBySignal.set(target, nodeId);\n this._indexNode(nodeId, componentId);\n return effect;\n },\n\n _graphNode(nodeId, entry, signal) {\n const node = { ...entry.meta };\n if (signal._isProxy) {\n const proxy = this._reactiveProxies.get(nodeId)?.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 return node;\n },\n\n // Reactive graph (§6.7 A/D). Caps are applied while building: past\n // maxNodes no node objects are created and only edges whose endpoints\n // are both included are emitted (the rest are counted in edgesOmitted).\n // - scoped (componentId): that component's nodes plus the tracked nodes\n // directly connected to them (via deps / reactions through untracked\n // intermediates). Dependencies are resolved through _idBySignal, so\n // the cost is O(scope + neighbours), not O(all nodes). The walk is\n // completed even past the cap, so total.nodes/edges are exact.\n // - global: nodes in registration order up to maxNodes ('global-head'),\n // with their incoming dependency edges. Nodes past the cap are not\n // dereferenced; total.nodes is the registered count (may include\n // nodes whose signal was already collected, nodesKind 'registered')\n // and total.edges is null when the walk stopped early (not guessed).\n getReactiveGraph(componentId, caps) {\n const scoped = componentId !== undefined && componentId !== null;\n const maxNodes = this._graphCap(caps && caps.maxNodes, 5000);\n const maxEdges = this._graphCap(caps && caps.maxEdges, 20000);\n const live = new Map();\n // Live signal of a tracked node, or null (forgets dead / unmounted nodes).\n const liveSignal = (nodeId) => {\n if (live.has(nodeId)) return live.get(nodeId);\n const entry = this._reactiveNodes.get(nodeId);\n let signal = entry ? entry.signal.deref() : null;\n if (entry && (!signal || !this._instances.has(entry.meta.componentId))) {\n this._forgetNode(nodeId);\n signal = null;\n }\n live.set(nodeId, signal || null);\n return signal || null;\n };\n const idOf = (dep) => {\n const id = this._idBySignal.get(dep);\n return id !== undefined && liveSignal(id) === dep ? id : null;\n };\n\n const included = new Set();\n const seen = new Set();\n const edges = [];\n const edgeSet = new Set();\n let edgesOmitted = 0;\n let truncated = false;\n const see = (id) => {\n seen.add(id);\n if (!included.has(id) && included.size < maxNodes) included.add(id);\n return included.has(id);\n };\n const addEdge = (from, to) => {\n const key = from + '>' + to;\n if (from === to || edgeSet.has(key)) return;\n edgeSet.add(key);\n const inFrom = see(from);\n const inTo = see(to);\n if (inFrom && inTo && edges.length < maxEdges) edges.push({ from, to });\n else edgesOmitted++;\n };\n // Walk a signal's neighbours (deps or reactions) through untracked\n // intermediates until tracked nodes are reached.\n const walk = (start, field, onHit) => {\n if (!start) return;\n const visited = new Set();\n const queue = [...start];\n // Index cursor instead of queue.shift(): shift is O(queue) per pop.\n for (let qi = 0; qi < queue.length; qi++) {\n const dep = queue[qi];\n if (!dep || visited.has(dep)) continue;\n visited.add(dep);\n const id = idOf(dep);\n if (id) onHit(id);\n else if (dep[field]) for (const d of dep[field]) queue.push(d);\n }\n };\n\n let totalNodes = 0;\n let totalEdges = null;\n if (scoped) {\n for (const nodeId of [...(this._nodesByComponent.get(componentId) || [])]) {\n const signal = liveSignal(nodeId);\n if (!signal) continue;\n see(nodeId);\n walk(signal.deps, 'deps', (depId) => addEdge(depId, nodeId));\n walk(signal.reactions, 'reactions', (rId) => addEdge(nodeId, rId));\n }\n totalNodes = seen.size;\n totalEdges = edgeSet.size;\n truncated = included.size < seen.size || edgesOmitted > 0;\n } else {\n let complete = true;\n for (const nodeId of [...this._reactiveNodes.keys()]) {\n if (included.size >= maxNodes && !included.has(nodeId)) {\n complete = false;\n break;\n }\n const signal = liveSignal(nodeId);\n if (!signal) continue;\n see(nodeId);\n walk(signal.deps, 'deps', (depId) => addEdge(depId, nodeId));\n }\n // registered count, minus nodes found dead while building\n totalNodes = this._reactiveNodes.size;\n totalEdges = complete ? edgeSet.size : null;\n truncated = !complete || edgesOmitted > 0;\n }\n\n const nodes = [];\n for (const nodeId of included) {\n const signal = liveSignal(nodeId);\n if (signal) nodes.push(this._graphNode(nodeId, this._reactiveNodes.get(nodeId), signal));\n }\n return {\n scope: scoped ? componentId : null,\n nodes,\n edges,\n total: { nodes: totalNodes, nodesKind: 'registered', edges: totalEdges },\n truncated,\n edgesOmitted,\n policy: scoped ? 'scoped' : 'global-head',\n };\n },\n\n _graphCap(value, max) {\n const n = Math.floor(Number(value));\n return Number.isFinite(n) && n >= 1 ? Math.min(n, max) : max;\n },\n\n // Request (§6.7 A + review M1): { requestId?, epoch?, componentId?,\n // maxNodes?, maxEdges? }. A request for another page load (epoch) is not\n // answered by this one; the reply echoes requestId and epoch. An old\n // server sends {} → global graph with the default caps (= its LIMITS).\n sendReactiveGraph(request) {\n const req = request || {};\n if (req.epoch !== undefined && req.epoch !== null && req.epoch !== this._epoch) return;\n const cid = typeof req.componentId === 'number' ? req.componentId : null;\n const graph = this.getReactiveGraph(cid, { maxNodes: req.maxNodes, maxEdges: req.maxEdges });\n graph.epoch = this._epoch;\n // freshness (review): when this answer was built; caches keep it\n graph.computedAt = Date.now();\n if (req.requestId !== undefined) graph.requestId = req.requestId;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-graph', graph);\n }\n },\n\n // Overview aggregate (§6.7 I/J). Built from the activity buckets of the\n // window (whole seconds: the current partial second plus the previous\n // ones, ceil(windowMs / 1000) buckets) — cost O(components active in the\n // window), no graph, no node scan. Rows rank by sampled changes, then\n // renders, then render ms; rows + other = total (components and nodes).\n getReactiveSummary(opts) {\n const o = opts || {};\n const clampInt = (v, lo, hi, d) => {\n const n = Math.floor(Number(v));\n return Number.isFinite(n) ? Math.min(hi, Math.max(lo, n)) : d;\n };\n const topK = clampInt(o.topK, 1, 200, 50);\n const windowMs = clampInt(o.windowMs, 1000, 60000, 10000);\n const nowSec = Math.floor(performance.now() / 1000);\n const agg = new Map();\n let sampledMs = 0;\n for (let k = 0; k < Math.ceil(windowMs / 1000); k++) {\n const sec = nowSec - k;\n const b = this._activity[((sec % 60) + 60) % 60];\n if (!b || b.second !== sec) continue;\n sampledMs += b.sampledMs;\n for (const [cid, r] of b.rows) {\n if (!this._instances.has(cid)) continue;\n let a = agg.get(cid);\n if (!a) {\n a = { changes: 0, renders: 0, renderMs: 0 };\n agg.set(cid, a);\n }\n a.changes += r.changes;\n a.renders += r.renders;\n a.renderMs += r.renderMs;\n }\n }\n const ranked = [...agg].sort(\n (x, y) =>\n y[1].changes - x[1].changes || y[1].renders - x[1].renders || y[1].renderMs - x[1].renderMs || x[0] - y[0],\n );\n let rowNodes = 0;\n const rows = ranked.slice(0, topK).map(([cid, a]) => {\n const n = this._nodeCounts.get(cid) || { state: 0, derived: 0, effect: 0 };\n rowNodes += n.state + n.derived + n.effect;\n return {\n componentId: cid,\n file: this._instances.get(cid).file,\n nodes: { state: n.state, derived: n.derived, effect: n.effect },\n changes: a.changes,\n renders: a.renders,\n renderMs: Math.round(a.renderMs * 1000) / 1000,\n };\n });\n const t = this._nodeTotals;\n const until = Date.now();\n return {\n epoch: this._epoch,\n window: { ms: windowMs, since: until - windowMs, until, sampledActiveMs: Math.min(windowMs, sampledMs) },\n policy: 'sampled-200ms',\n coverage: 'component-init',\n components: { total: this._instances.size, withActivity: agg.size },\n rows,\n other: { components: this._instances.size - rows.length, nodes: t.state + t.derived + t.effect - rowNodes },\n truncated: agg.size > topK,\n capabilities: { valueInspection: false, signalHistory: false, writeCause: false },\n // Seed coverage: changes of pending nodes are not observable yet.\n baseline: this._baselineInfo(),\n };\n },\n\n // Request { requestId?, epoch?, topK?, windowMs? } (same correlation as\n // the graph, review M1).\n sendReactiveSummary(request) {\n const req = request || {};\n if (req.epoch !== undefined && req.epoch !== null && req.epoch !== this._epoch) return;\n const summary = this.getReactiveSummary(req);\n if (req.requestId !== undefined) summary.requestId = req.requestId;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-summary', summary);\n }\n },\n\n // --- Phase 3: State Timeline ---\n\n // Current value of a $state node: the signal value, or for tag_proxy\n // nodes (non-reassigned object/array state) the live proxy itself.\n // Returns a sentinel when the value can't be read.\n _readStateValue(nodeId, signal) {\n if (signal._isProxy) {\n const proxy = this._reactiveProxies.get(nodeId)?.deref();\n return proxy === undefined ? __NO_VALUE : proxy;\n }\n const v = signal.v;\n // Svelte's UNINITIALIZED marker is a symbol; functions aren't state data.\n return typeof v === 'symbol' || typeof v === 'function' ? __NO_VALUE : v;\n },\n\n // Runs every 200 ms in the user's app, so it must stay cheap regardless of\n // how much state the app holds (§6.7 D).\n // 1. Hint pass, time-sliced: at most HINT_NODES $state nodes per tick,\n // continuing from a cursor carried across ticks, so a full sweep over\n // n nodes takes ceil(n / HINT_NODES) ticks (_pollSweep.lastMs is the\n // measured sweep time = worst-case detection delay). Per node O(1):\n // primitives are compared directly and recorded at once; objects\n // whose reference or Svelte write version (wv) changed are added to\n // _pollDirty, which persists across ticks until serialized.\n // 2. Serialization work is limited to BUDGET_MS per tick (dirty nodes\n // carry over, nothing is dropped).\n // 3. In-place deep mutations (proxies don't bump an outer wv) are found by\n // time-based re-checks: each object is re-verified no earlier than\n // nextCheckAt = now + max(RECHECK_MS, 50 x its serialization cost),\n // paid from a DEEP_MS-per-tick credit, so the amortized cost is bounded\n // however many large states the app holds.\n // 4. Values serializing above MAX_SNAPSHOT_CHARS are kept as length+hash\n // (not the string) and appear in the timeline as a summary.\n // Writes between two visits of a node are observed as one change (the\n // timeline is a sample, policy 'sampled-200ms'; not counted as dropped).\n _pollStateValues(baseline) {\n const HINT_NODES = 4096;\n const BUDGET_MS = 2;\n const DEEP_MS = 0.5;\n const now = performance.now();\n if (!baseline) {\n this._deepCredit = Math.min(10 * DEEP_MS, (this._deepCredit || 0) + DEEP_MS);\n // this tick samples 200 ms of wall time (window.sampledActiveMs)\n const bucket = this._bucket();\n bucket.sampledMs = Math.min(1000, bucket.sampledMs + 200);\n }\n const ids = this._pollIds;\n const due = [];\n const limit = Math.min(HINT_NODES, ids.length);\n for (let visited = 0; visited < limit && ids.length > 0; visited++) {\n if (this._pollCursor >= ids.length) {\n this._pollCursor = 0;\n if (!baseline) {\n if (this._pollSweep.startedAt) this._pollSweep.lastMs = now - this._pollSweep.startedAt;\n this._pollSweep.startedAt = now;\n this._pollSweep.nodes = ids.length;\n }\n }\n if (!baseline && !this._pollSweep.startedAt) this._pollSweep.startedAt = now;\n const nodeId = ids[this._pollCursor];\n const entry = this._reactiveNodes.get(nodeId);\n if (!entry || entry.meta.type !== 'state') {\n // re-tracked as another type: drop from the poll list (slot reused)\n this._pollRemove(nodeId);\n continue;\n }\n if (!this._instances.has(entry.meta.componentId)) {\n this._forgetNode(nodeId); // swaps the last id into this slot\n continue;\n }\n this._pollCursor++;\n const signal = entry.signal.deref();\n const value = signal ? this._readStateValue(nodeId, signal) : __NO_VALUE;\n if (value === __NO_VALUE) {\n // nothing readable (collected, uninitialized, function): not pending\n this._unseeded.delete(nodeId);\n continue;\n }\n if (value === null || typeof value !== 'object') {\n const had = this._stateSnapshots.has(nodeId);\n const prev = this._stateSnapshots.get(nodeId);\n if (had && Object.is(prev, value)) continue;\n // Forget the object key so object -> primitive -> same object again\n // is still recorded as a change.\n if (this._stateSnapshotStrs) this._stateSnapshotStrs.delete(nodeId);\n this._pollDirty.delete(nodeId);\n if (!had) {\n // first observation: seed only\n this._stateSnapshots.set(nodeId, value);\n this._unseeded.delete(nodeId);\n continue;\n }\n this._recordChange(nodeId, entry, prev, value, typeof value === 'string' ? 2 * value.length : 64);\n continue;\n }\n let meta = this._pollMeta.get(nodeId);\n if (!meta) {\n meta = { ref: undefined, wv: undefined, nextCheckAt: 0 };\n this._pollMeta.set(nodeId, meta);\n }\n if (meta.ref !== value || meta.wv !== signal.wv) this._pollDirty.add(nodeId);\n else if (now >= meta.nextCheckAt) due.push(nodeId);\n }\n const start = performance.now();\n for (const nodeId of this._pollDirty) {\n if (performance.now() - start > BUDGET_MS) return;\n this._pollDirty.delete(nodeId);\n this._serializeNode(nodeId, false);\n }\n for (const nodeId of due) {\n if (this._deepCredit <= 0 || performance.now() - start > BUDGET_MS) return;\n this._serializeNode(nodeId, true);\n }\n },\n\n // Activation (late consumer): nodes registered while inactive have no\n // seed yet, so a write before their first visit could not be seen as a\n // change. The first slice is seeded at activation instead of 200 ms later:\n // ONE normal pass (<= HINT_NODES nodes, the per-tick serialization budget;\n // the rest follow on the usual ticks). Seeds record nothing; readiness is\n // disclosed in the summary as baseline { complete, pendingNodes }.\n _pollBaseline() {\n this._baselining = true;\n try {\n this._pollStateValues(true);\n } catch { /* never break activation */ } finally {\n this._baselining = false;\n }\n },\n\n _serializeNode(nodeId, deep) {\n const MAX_SNAPSHOT_CHARS = 32768;\n const RECHECK_MS = 1000;\n const entry = this._reactiveNodes.get(nodeId);\n const meta = this._pollMeta.get(nodeId);\n if (!entry || !meta) return;\n const signal = entry.signal.deref();\n if (!signal) return;\n const value = this._readStateValue(nodeId, signal);\n if (value === __NO_VALUE || value === null || typeof value !== 'object') return;\n try {\n const t0 = performance.now();\n const str = JSON.stringify(value);\n const cost = performance.now() - t0;\n if (deep) this._deepCredit -= cost;\n meta.ref = value;\n meta.wv = signal.wv;\n meta.nextCheckAt = t0 + Math.max(RECHECK_MS, 50 * cost);\n // Small values keep the string (exact compare); large ones only a\n // length+hash so a 5 MB array doesn't pin 5 MB per node.\n const key = str === undefined ? '' : str.length > MAX_SNAPSHOT_CHARS ? str.length + ':' + __hash(str) : str;\n if (!this._stateSnapshotStrs) this._stateSnapshotStrs = new Map();\n const hadKey = this._stateSnapshotStrs.has(nodeId);\n if (hadKey && this._stateSnapshotStrs.get(nodeId) === key) return;\n this._stateSnapshotStrs.set(nodeId, key);\n const tooLarge = str !== undefined && str.length > MAX_SNAPSHOT_CHARS;\n const snapshot =\n str === undefined\n ? null\n : tooLarge\n ? '(object: ' + str.length + ' chars, too large to snapshot)'\n : JSON.parse(str);\n const size = typeof snapshot === 'string' ? 64 : str.length;\n if (!this._stateSnapshots.has(nodeId)) {\n // first observation: seed only (no entry, no counters)\n this._stateSnapshots.set(nodeId, snapshot);\n this._lastSnapshotSize.set(nodeId, size);\n this._unseeded.delete(nodeId);\n return;\n }\n if (tooLarge) this._valueTooLarge++;\n const prev = this._stateSnapshots.get(nodeId);\n this._recordChange(nodeId, entry, prev, snapshot, size + (this._lastSnapshotSize.get(nodeId) || 0));\n this._lastSnapshotSize.set(nodeId, size);\n } catch {\n // non-serializable (cycles, BigInt): never observable, so not pending\n this._unseeded.delete(nodeId);\n }\n },\n\n _recordChange(nodeId, entry, oldValue, newValue, approxBytes) {\n const change = {\n id: nodeId,\n name: entry.meta.name,\n componentFile: entry.meta.componentFile,\n oldValue,\n newValue,\n timestamp: Date.now(),\n };\n const bytes = (approxBytes || 64) + 192;\n // seq stays runtime-internal: the server assigns its own seq (§6.4).\n this._entryInfo.set(change, { seq: ++this._timelineSeq, bytes });\n this._stateTimeline.push(change);\n this._timelineBytes += bytes;\n // sampled change count (§6.7 J IA2): independent of the ring\n this._activityRow(entry.meta.componentId).changes++;\n this._stateSnapshots.set(nodeId, newValue);\n // The ring (_trimTimeline) is applied in bulk, not with one splice per\n // change (a poll tick can record thousands): before every read (each\n // push — with a hot channel and an active consumer, at least every 200\n // changes — the full snapshot, getStateTimeline() and clear), and here\n // once more than __TIMELINE_TRIM_AT entries are held or the byte budget\n // is exceeded, which bounds memory when nothing reads or pushes.\n if (this._stateTimeline.length > __TIMELINE_TRIM_AT || this._timelineBytes > __TIMELINE_BYTES) {\n this._trimTimeline();\n }\n this._scheduleTimelineUpdate();\n },\n\n // Ring: at most 500 entries and __TIMELINE_BYTES (the newest entry is\n // always kept). Removed entries that were never pushed are disclosed\n // as dropped (§6.7 C); pushed ones are already on the server.\n _trimTimeline() {\n const all = this._stateTimeline;\n let cut = 0;\n let bytesLeft = this._timelineBytes;\n while (cut < all.length - 1 && (all.length - cut > 500 || bytesLeft > __TIMELINE_BYTES)) {\n const info = this._entryInfo.get(all[cut]);\n const reason = all.length - cut > 500 ? 'runtime-count' : 'runtime-bytes';\n if (info.seq > this._pushedSeq) this._timelineDropped[reason]++;\n bytesLeft -= info.bytes;\n cut++;\n }\n if (cut > 0) {\n all.splice(0, cut);\n this._timelineBytes = bytesLeft;\n }\n },\n\n // Push (§6.7 C): a throttle, not a debounce — a pending timer is never\n // re-armed, so unsent entries leave within 300 ms of the first one even\n // under continuous change — plus an immediate flush once 200 entries\n // (one message) are unsent, since one poll tick can record thousands.\n _scheduleTimelineUpdate() {\n if (!this._active || this._baselining) return;\n if (this._timelineSeq - this._pushedSeq >= 200) {\n this._flushTimeline();\n return;\n }\n if (this._timelineDebounceTimer) return;\n this._timelineDebounceTimer = setTimeout(() => {\n this._timelineDebounceTimer = null;\n this._flushTimeline();\n }, 300);\n },\n\n _flushTimeline() {\n if (this._timelineDebounceTimer) {\n clearTimeout(this._timelineDebounceTimer);\n this._timelineDebounceTimer = null;\n }\n if (!import.meta.hot) return;\n for (const msg of this._timelineDeltas()) {\n import.meta.hot.send('svelte-devtools:state-timeline', msg);\n }\n },\n\n // Disclosure counters since the previous push, attached to (and reset\n // by) the next message: dropped [{ reason, count }] and valueTooLarge.\n _takeTimelineDisclosure(msg) {\n msg.baseline = this._baselineInfo();\n this._sentPendingNodes = msg.baseline.pendingNodes;\n const dropped = [];\n for (const reason of ['runtime-count', 'runtime-bytes']) {\n const count = this._timelineDropped[reason];\n if (count > 0) dropped.push({ reason, count });\n this._timelineDropped[reason] = 0;\n }\n if (dropped.length) msg.dropped = dropped;\n if (this._valueTooLarge > 0) msg.valueTooLarge = this._valueTooLarge;\n this._valueTooLarge = 0;\n return msg;\n },\n\n // Seed coverage (review B-2), on every state-timeline message and the\n // summary: pendingNodes = polled $state nodes without a first sample;\n // their writes are not observable yet.\n _baselineInfo() {\n return { complete: this._unseeded.size === 0, pendingNodes: this._unseeded.size };\n },\n\n _hasTimelineDisclosure() {\n return (\n this._sentPendingNodes !== this._unseeded.size ||\n this._timelineDropped['runtime-count'] > 0 ||\n this._timelineDropped['runtime-bytes'] > 0 ||\n this._valueTooLarge > 0\n );\n },\n\n // Delta push (docs/devframe-migration.md §6.4): only entries recorded\n // since the previous push, ≤ 200 per message, tagged with a per-page-load\n // epoch; reset after clearStateTimeline(). A message with no changes is\n // still sent when there is something to disclose (all unsent dropped).\n _timelineDeltas() {\n this._trimTimeline();\n const all = this._stateTimeline;\n let start = all.length;\n while (start > 0 && this._entryInfo.get(all[start - 1]).seq > this._pushedSeq) start--;\n const pending = all.slice(start);\n const msgs = [];\n const first = this._timelineReset || this._hasTimelineDisclosure();\n for (let i = 0; i < pending.length || (first && i === 0); i += 200) {\n const msg = { epoch: this._epoch, changes: pending.slice(i, i + 200) };\n if (i === 0 && this._timelineReset) msg.reset = true;\n if (i === 0) this._takeTimelineDisclosure(msg);\n else msg.baseline = this._baselineInfo();\n msgs.push(msg);\n }\n this._timelineReset = false;\n this._pushedSeq = this._timelineSeq;\n return msgs;\n },\n\n // Full buffer (reset: true) — reply to request-state-timeline and the\n // activation snapshot. Newest entries first up to __TIMELINE_BYTES, the\n // server's own buffer budget, so nothing is sent that it would drop.\n _timelineFull() {\n this._trimTimeline();\n const all = this._stateTimeline;\n let start = all.length;\n let bytes = 0;\n while (start > 0) {\n const size = this._entryInfo.get(all[start - 1]).bytes;\n if (bytes + size > __TIMELINE_BYTES && start < all.length) break;\n bytes += size;\n start--;\n }\n this._timelineReset = false;\n this._pushedSeq = this._timelineSeq;\n return this._takeTimelineDisclosure({ epoch: this._epoch, changes: all.slice(start), reset: true });\n },\n\n getStateTimeline() {\n this._trimTimeline();\n return this._stateTimeline;\n },\n\n clearStateTimeline() {\n // Ring first: unsent entries past the cap are disclosed as dropped,\n // exactly as if they had been removed when recorded.\n this._trimTimeline();\n this._stateTimeline = [];\n this._timelineBytes = 0;\n this._pushedSeq = this._timelineSeq;\n this._timelineReset = true;\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(gen) {\n if (gen !== this._fpsGen) return;\n this._fpsFrameTimes.push(performance.now());\n requestAnimationFrame(() => this._fpsLoop(gen));\n },\n\n _isVisible() {\n return typeof document === 'undefined' || document.visibilityState !== 'hidden';\n },\n\n _setSubscription(data) {\n const wasActive = this._active;\n this._active = !!(data && data.active);\n this._componentDeltas = !!(data && data.componentDeltas);\n if (!this._active) {\n this._pendingAdded.clear();\n this._pendingRemoved.clear();\n }\n // resync: the server holds no full base for this epoch (§6.5, review\n // C1), or we reconnected to a possibly restarted server.\n const resync = this._resync || !!(data && data.resync);\n this._resync = false;\n if (this._active && !wasActive) this._pollBaseline();\n if (this._active && (!wasActive || resync)) {\n // Collector state may be stale or empty (first activation, resync, or a\n // reconnect to a possibly restarted server): resend everything once, in\n // the order components -> profiles -> timeline(reset: true); the\n // reset push marks the activation snapshot complete (D4).\n this._sendUpdate(true);\n this._sendProfileUpdate();\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:state-timeline', this._timelineFull());\n }\n }\n this._syncSampling();\n },\n\n // Sampling (state poll + FPS) runs only while active AND the app tab is\n // visible.\n _syncSampling() {\n const want = this._active && this._isVisible();\n if (want && !this._sampling) {\n this._fpsFrameTimes = [];\n const gen = ++this._fpsGen;\n this._sampling = {\n poll: setInterval(() => {\n this._pollStateValues();\n // seed progress changed: push it even without changes (B-2)\n if (this._sentPendingNodes !== this._unseeded.size) this._scheduleTimelineUpdate();\n }, 200),\n fps: setInterval(() => { this._sampleFps(); }, 500),\n };\n requestAnimationFrame(() => this._fpsLoop(gen));\n } else if (!want && this._sampling) {\n clearInterval(this._sampling.poll);\n clearInterval(this._sampling.fps);\n this._sampling = null;\n this._fpsGen++;\n this._fpsFrameTimes = [];\n }\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 // State polling and FPS sampling start on the server's subscription\n // message (§6.3) and pause while the app tab is hidden.\n if (typeof document !== 'undefined' && document.addEventListener) {\n document.addEventListener('visibilitychange', () => __SVELTE_DT._syncSampling());\n }\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 // Pull requests are answered regardless of the subscription state.\n if (import.meta.hot) {\n import.meta.hot.on('svelte-devtools:subscription', (data) => {\n __SVELTE_DT._setSubscription(data);\n });\n import.meta.hot.on('vite:ws:connect', () => {\n // The server may have restarted with an empty collector: the reply to\n // runtime-ready is treated as a fresh activation (full snapshot).\n __SVELTE_DT._resync = true;\n import.meta.hot.send('svelte-devtools:runtime-ready', {});\n });\n import.meta.hot.send('svelte-devtools:runtime-ready', {});\n import.meta.hot.on('svelte-devtools:request-reactive-graph', (data) => {\n __SVELTE_DT.sendReactiveGraph(data);\n });\n import.meta.hot.on('svelte-devtools:request-reactive-summary', (data) => {\n __SVELTE_DT.sendReactiveSummary(data);\n });\n import.meta.hot.on('svelte-devtools:request-state-timeline', () => {\n import.meta.hot.send('svelte-devtools:state-timeline', __SVELTE_DT._timelineFull());\n });\n import.meta.hot.on('svelte-devtools:clear-state-timeline', () => {\n __SVELTE_DT.clearStateTimeline();\n });\n }\n}\n", C = {
213
+ var S = "virtual:svelte-devtools-runtime", ge = "\0virtual:svelte-devtools-runtime", _e = "\0svelte-devtools:wrapped-client", ve = "\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 context -> devtools id (review B1b). A component created later\n// by a block update ({#each}/{#if} after mount) runs outside its parent's\n// push..pop, where the runtime's init stack is empty (the wrapper stack\n// below covers the wrapped block helpers; this covers the rest). Svelte (5.56.8,\n// internal/client) exports active_effect; every effect records the\n// component context it was created in (effect.ctx) and runs with that\n// context restored. The block effect that creates the child belongs to the\n// parent's template, so active_effect.ctx at push time IS the parent's\n// context. Each context is mapped to its id once the component mounted\n// (inside its own deferred effect, where active_effect.ctx is the\n// component's own context). The map lives on the runtime (dt._idByContext)\n// so the reactive graph can name the component of any effect it reaches.\n// Weak map: no leak, no Svelte state touched.\nfunction __parentFromContext() {\n const dt = __dt();\n const effect = __svelte_original.active_effect;\n const ctx = effect ? effect.ctx : null;\n return dt && ctx && dt._idByContext ? dt._idByContext.get(ctx) : undefined;\n}\n\n// Owner of a signal/effect created right now: the component whose init (or\n// wrapped block callback) is running, else — outside init, e.g. an effect\n// created while another effect runs — the component of the active effect.\nfunction __owner() {\n const cid = __currentId();\n if (cid !== null) return cid;\n const fromContext = __parentFromContext();\n return fromContext === undefined ? null : fromContext;\n}\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 // Parent hint, read before this component's own id exists:\n // 1. the wrapper stack: the component whose init is running (children\n // rendered directly by a template, where active_effect still belongs to\n // an outer block), or the block owner re-pushed by __wrapBlockFn for a\n // later {#each}/{#if}/{#key} update (see Block Attribution below);\n // 2. else the context-derived owner, for re-creation paths the wrapper\n // does not wrap (snippet, svelte:element, ...).\n let parentHint = null;\n try { parentHint = __currentId(); } catch {}\n if (parentHint === null) {\n try { parentHint = __parentFromContext(); } catch {}\n }\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 dt._pendingParent = parentHint;\n const id = dt.register(file);\n __idStack.push(id);\n dt.startInit(id);\n try {\n __svelte_original.user_effect(() => {\n try {\n const own = __svelte_original.active_effect;\n if (own && own.ctx) dt._idByContext.set(own.ctx, id);\n if (own) dt.setComponentEffect(id, own);\n } catch {}\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 = __owner();\n const type = (__pendingSignal.ref === signal) ? __pendingSignal.type : 'state';\n if (dt && cid !== null) {\n const owner = __svelte_original.active_effect;\n if (type === 'derived') {\n dt.trackDerived(signal, name, cid, owner);\n } else {\n dt.trackState(signal, name, cid, owner);\n }\n } else if (dt && dt._moduleFile) {\n // created while a .svelte.js/.ts module body runs (shared state)\n dt.trackModuleSignal(type === 'derived' ? 'derived' : 'state', signal, name);\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 = __owner();\n if (dt && cid !== null) {\n dt.trackProxy(proxy, name, cid, __svelte_original.active_effect);\n } else if (dt && dt._moduleFile) {\n dt.trackModuleSignal('proxy', proxy, name);\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\n// --- Effect Tracking ---\n//\n// A top-level $effect in a component is deferred by Svelte until mount:\n// user_effect() returns undefined and the effect object is created later,\n// in pop(). So the node is registered now (named per component) and bound\n// to the real effect object on its first run, where active_effect IS the\n// effect; the Svelte runtime then keeps effect.deps current, which is what\n// getReactiveGraph() reads to build edges. The wrapped callback forwards\n// this, arguments and the return value (teardown) unchanged.\n\nfunction __trackUserEffect(original, kind, args) {\n let cid = null;\n let dt = null;\n let fn = null;\n try {\n dt = __dt();\n fn = args[0];\n if (dt && typeof fn === 'function') cid = __owner();\n } catch {}\n if (cid === null) return original.apply(null, args);\n let nodeId = null;\n let bound = false;\n const wrapped = Array.prototype.slice.call(args);\n wrapped[0] = function () {\n if (!bound) {\n bound = true;\n try { dt.bindEffect(nodeId, __svelte_original.active_effect); } catch {}\n }\n return fn.apply(this, arguments);\n };\n try { nodeId = dt.trackUserEffect(kind, cid, __svelte_original.active_effect); } catch {}\n const result = original.apply(null, wrapped);\n if (result && !bound) {\n bound = true;\n try { dt.bindEffect(nodeId, result); } catch {}\n }\n return result;\n}\n\nexport function user_effect() {\n return __trackUserEffect(__svelte_original.user_effect, 'effect', arguments);\n}\n\nexport function user_pre_effect() {\n return __trackUserEffect(__svelte_original.user_pre_effect, 'effect_pre', arguments);\n}\n\n// --- Render Profiling ---\n//\n// Svelte 5 has no virtual DOM: the compiler wraps every dynamic part of a\n// template in a template_effect (deferred_template_effect for <svelte:head>\n// <title>). A re-run of such an effect IS the component updating the DOM, so\n// these two functions are the measurement points for renders / render time.\n//\n// The first run of an effect is the mount-time paint, already covered by\n// initTime, so it is skipped (initialRun). One state change re-runs several\n// effects of the same component, so durations are pooled per microtask and\n// recorded once per flush: recordRender() +1, recordRenderTime() the sum.\n\nconst __pendingRenderDurations = new Map();\nlet __renderFlushScheduled = false;\n\nfunction __flushRenderDurations() {\n __renderFlushScheduled = false;\n const dt = __dt();\n for (const [cid, duration] of __pendingRenderDurations) {\n if (dt) {\n try { dt.recordRender(cid); } catch {}\n try { dt.recordRenderTime(cid, duration); } catch {}\n }\n }\n __pendingRenderDurations.clear();\n}\n\nfunction __recordRenderDuration(cid, duration) {\n __pendingRenderDurations.set(cid, (__pendingRenderDurations.get(cid) || 0) + duration);\n if (!__renderFlushScheduled) {\n __renderFlushScheduled = true;\n queueMicrotask(__flushRenderDurations);\n }\n}\n\n// Returns a (possibly new) args array whose first element times each re-run\n// of the effect closure. The owning component is whatever id is on top of\n// __idStack at effect-creation time. User exceptions propagate untouched;\n// only the args are built inside try so the original is called exactly once.\nfunction __wrapTemplateEffect(args) {\n const cid = __owner();\n const fn = args[0];\n if (cid === null || typeof fn !== 'function') return args;\n const wrapped = Array.prototype.slice.call(args);\n let initialRun = true;\n wrapped[0] = function () {\n if (initialRun) {\n initialRun = false;\n return fn.apply(this, arguments);\n }\n const start = performance.now();\n try {\n return fn.apply(this, arguments);\n } finally {\n try { __recordRenderDuration(cid, performance.now() - start); } catch {}\n }\n };\n return wrapped;\n}\n\nexport function template_effect() {\n let args = arguments;\n try { args = __wrapTemplateEffect(arguments); } catch {}\n return __svelte_original.template_effect.apply(null, args);\n}\n\nexport function deferred_template_effect() {\n let args = arguments;\n try { args = __wrapTemplateEffect(arguments); } catch {}\n return __svelte_original.deferred_template_effect.apply(null, args);\n}\n\n// --- Block Attribution ---\n//\n// Svelte creates render effects synchronously at creation time. At mount this\n// happens during the component function (between push/pop), so __idStack has\n// the owner. But effects for {#each} items added later or re-created {#if}\n// branches are created during a batch flush, when the stack is empty — they\n// would never be attributed. Block helpers themselves ARE always called\n// during their owner's init (or inside an outer block's callback), so the\n// owner id is on the stack at block-creation time. __wrapBlock captures it\n// and re-pushes it around every function argument the block invokes later,\n// so effects created inside land on the innermost owner. Child components\n// push their own id on top, keeping nested attribution correct.\n\n// A component whose init throws (caught by <svelte:boundary>, or by the\n// block's caller) never reaches pop(): its id would stay on both stacks and\n// every later signal/child would be attributed to it. On the way out — normal\n// or thrown — the stack is cut back to its depth on entry, and ids left\n// above it are dropped as aborted inits (registered, never mounted).\nfunction __wrapBlockFn(fn, cid) {\n return function () {\n let depth = -1;\n try { depth = __idStack.length; __idStack.push(cid); } catch {}\n try {\n return fn.apply(this, arguments);\n } finally {\n if (depth !== -1) {\n try {\n if (__idStack.length > depth + 1) {\n const dt = __dt();\n const aborted = __idStack.slice(depth + 1);\n for (let i = aborted.length - 1; i >= 0; i--) {\n try { if (dt) dt.abortInit(aborted[i]); } catch {}\n }\n }\n __idStack.length = depth;\n } catch {}\n }\n }\n };\n}\n\nfunction __wrapBlock(args) {\n const cid = __owner();\n if (cid === null) return args;\n const wrapped = Array.prototype.slice.call(args);\n for (let i = 0; i < wrapped.length; i++) {\n if (typeof wrapped[i] === 'function') wrapped[i] = __wrapBlockFn(wrapped[i], cid);\n }\n return wrapped;\n}\n\nexport function each() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.each.apply(null, args);\n}\n\nexport function key() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.key.apply(null, args);\n}\n\nexport function component() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.component.apply(null, args);\n}\n\nexport function boundary() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original.boundary.apply(null, args);\n}\n\n// 'if' and 'await' are reserved words, so they cannot be declared as\n// function names and are exported via aliases instead.\nfunction __if_block() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original['if'].apply(null, args);\n}\n\nfunction __await_block() {\n let args = arguments;\n try { args = __wrapBlock(arguments); } catch {}\n return __svelte_original['await'].apply(null, args);\n}\n\nexport { __if_block as if, __await_block as await };\n", ye = "\nif (typeof window !== 'undefined' && !window.__SVELTE_DEVTOOLS__) {\n const __NO_VALUE = Symbol('no value');\n // State timeline ring (§6.7 C): every read sees at most 500 entries and\n // __TIMELINE_BYTES (also the server's buffer budget). The raw array may\n // hold up to __TIMELINE_TRIM_AT (2 x the 500 cap) between bulk trims.\n const __TIMELINE_BYTES = 4 * 1024 * 1024;\n const __TIMELINE_TRIM_AT = 1000;\n // FNV-1a over UTF-16 code units: cheap change detection for large snapshots.\n const __hash = (str) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(36);\n };\n const __SVELTE_DT = {\n _nextId: 0,\n _instances: new Map(),\n _stack: [],\n _pendingParent: undefined,\n _pendingFile: null,\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 // componentId -> Set<nodeId>. Lets unmount purge a component's reactive\n // nodes without scanning every node of the app (was O(components x nodes)).\n _nodesByComponent: new Map(),\n\n // Phase 3: State timeline\n _stateTimeline: [],\n _stateSnapshots: new Map(),\n _timelineDebounceTimer: null,\n // signal (or proxy marker) -> nodeId, set at track time and removed by\n // _forgetNode: a scoped graph resolves dependencies without scanning\n // every node (§6.7 D).\n _idBySignal: new WeakMap(),\n // proxy -> nodeId (tag_proxy nodes; the marker above is per node)\n _idByProxy: new WeakMap(),\n // Svelte component context -> component id (set by the wrapper on mount)\n _idByContext: new WeakMap(),\n // componentId -> (name -> last suffix) for names declared more than once\n // per instance ({@const} in {#each} items, several $state class instances)\n _nameSeq: new Map(),\n // Module scopes: signals created while a .svelte.js/.ts module body runs\n // (shared state such as `export const cart = $state(...)`) belong to a\n // scope per module file. Ids come from the component id counter (never\n // reused, non-negative) but scopes are not components: they are not in\n // _instances / the component tree, and live as long as the page.\n _modules: new Map(),\n _moduleByFile: new Map(),\n // $state node name -> (scope id -> Set<nodeId>): resolves a proxy's property source\n // (labelled '<name>.prop' by Svelte in dev) to the node of its proxy —\n // a tag_proxy node, or a state signal holding a proxy (reassigned object\n // state, class fields)\n _proxyNames: new Map(),\n // file of the module body running now (set by the module transform)\n _moduleFile: null,\n // componentId -> { effect, effect_pre } counters: effect names are\n // numbered per component, so instances of one file match\n _effectSeq: new Map(),\n // Every tracked node id (dense, swap-remove like _pollIds) for the\n // incremental liveness sweep, and its cursor.\n _sweepIds: [],\n _sweepIndex: new Map(),\n _sweepCursor: 0,\n // Polling bookkeeping (§6.7 D): $state node ids in a dense array kept up\n // to date incrementally (_pollIndex: nodeId -> position; removal is\n // swap-with-last), a cursor carried across ticks, nodes found changed but\n // not yet serialized (_pollDirty), and per-node change hints\n // (nodeId -> { ref, wv, nextCheckAt }).\n _pollIds: [],\n _pollIndex: new Map(),\n _pollCursor: 0,\n _pollDirty: new Set(),\n // Full-sweep bookkeeping: how long the last complete pass over all $state\n // nodes took (ms), for disclosure (= the worst-case detection delay).\n _pollSweep: { startedAt: 0, lastMs: null, nodes: 0 },\n _pollMeta: new Map(),\n _deepCredit: 0,\n _lastSnapshotSize: new Map(),\n _timelineSeq: 0,\n _pushedSeq: 0,\n _timelineReset: false,\n _entryInfo: new WeakMap(),\n // Sum of _entryInfo.bytes over _stateTimeline (runtime byte budget).\n _timelineBytes: 0,\n // Disclosure since the last push (§6.7 C): unsent entries the ring removed\n // (by count / by bytes) and changes recorded only as a size summary.\n _timelineDropped: { 'runtime-count': 0, 'runtime-bytes': 0 },\n _valueTooLarge: 0,\n\n // Overview aggregate (§6.7 I/J), O(1) per event: registered nodes per\n // component and type (+ app totals), and per-component activity in a\n // ring of 60 one-second buckets (sampled changes, renders, render ms,\n // plus how long sampling ran in that second).\n _nodeCounts: new Map(),\n _nodeTotals: { state: 0, derived: 0, effect: 0 },\n _activity: [],\n _epoch: Math.random().toString(36).slice(2) + Date.now().toString(36),\n\n // --- Activity subscription (docs/devframe-migration.md §6.3) ---\n // Inactive until the server says a DevTools client (UI tab or MCP lease)\n // is watching: no state polling, FPS rAF loop, or periodic HMR sends.\n _active: false,\n _componentsCost: { ms: 0, bytes: 0 },\n _componentDeltas: false,\n _pendingAdded: new Set(),\n _pendingRemoved: new Set(),\n _lastFullAt: 0,\n _resync: false,\n _profilesCost: { ms: 0, bytes: 0 },\n // = collector LIMITS.renderProfiles (pinned by a unit test): the server\n // keeps only the newest entries, so sending more is wasted bytes.\n _profileCap: 5000,\n _sampling: null,\n _baselining: false,\n // Polled state nodes not observed yet (no seed value). Their first\n // observation is a seed, not a change: no timeline entry, no activity\n // count, no drops — the old value is unknown, not null (review B-1/B-2).\n _unseeded: new Set(),\n // pendingNodes last sent on a state-timeline message (null = none yet)\n _sentPendingNodes: null,\n _fpsGen: 0,\n\n register(file) {\n const id = this._nextId++;\n // Parent: the wrapper's context-derived hint (components created by a\n // later block update, review B1b) when it names a live instance, else\n // the synchronous init stack (children created during the parent's own\n // init, before the parent is mapped).\n const hint = this._pendingParent;\n this._pendingParent = undefined;\n const parentId =\n hint !== undefined && hint !== null && this._instances.has(hint)\n ? hint\n : this._stack.length > 0 ? this._stack[this._stack.length - 1] : null;\n const name = file.split('/').pop()?.replace('.svelte', '') || 'Unknown';\n // children is a Set so unmounting one row of a 5 000-row list is O(1).\n this._instances.set(id, { id, file, name, parentId, mounted: true, children: new Set() });\n if (parentId !== null) {\n const parent = this._instances.get(parentId);\n if (parent) parent.children.add(id);\n }\n this._stack.push(id);\n if (this._active) this._pendingAdded.add(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 if (this._active) this._pendingAdded.add(id);\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) parent.children.delete(id);\n }\n this._removeChildren(id);\n this._cleanupComponent(id);\n }\n this._scheduleUpdate();\n this._scheduleProfileUpdate();\n },\n\n // Remounts get a fresh id, so entries keyed by a dead id would otherwise\n // survive forever — every per-component map must be purged here.\n _cleanupComponent(id) {\n // Delta bookkeeping: an id added and removed between two pushes was\n // never sent, so it cancels out.\n if (this._active && !this._pendingAdded.delete(id)) this._pendingRemoved.add(id);\n this._cleanupReactiveNodes(id);\n this._effectSeq.delete(id);\n this._nameSeq.delete(id);\n this._instances.delete(id);\n this._profiles.delete(id);\n this._initStartTimes.delete(id);\n },\n\n _cleanupReactiveNodes(componentId) {\n const nodeIds = this._nodesByComponent.get(componentId);\n if (!nodeIds) return;\n this._nodesByComponent.delete(componentId);\n for (const nodeId of [...nodeIds]) this._forgetNode(nodeId);\n },\n\n _forgetNode(nodeId) {\n const entry = this._reactiveNodes.get(nodeId);\n if (entry) {\n this._countNode(entry.meta.componentId, entry.meta.type, -1);\n const set = this._nodesByComponent.get(entry.meta.componentId);\n if (set) set.delete(nodeId);\n }\n const signal = entry && entry.signal.deref();\n if (signal && this._idBySignal.get(signal) === nodeId) this._idBySignal.delete(signal);\n const proxy = this._reactiveProxies.get(nodeId)?.deref();\n if (proxy && this._idByProxy.get(proxy) === nodeId) this._idByProxy.delete(proxy);\n if (entry && entry.meta.type === 'state') {\n const named = this._proxyNames.get(entry.meta.name);\n const inScope = named && named.get(entry.meta.componentId);\n if (inScope) {\n inScope.delete(nodeId);\n if (inScope.size === 0) named.delete(entry.meta.componentId);\n if (named.size === 0) this._proxyNames.delete(entry.meta.name);\n }\n }\n this._pollRemove(nodeId);\n this._sweepRemove(nodeId);\n this._pollDirty.delete(nodeId);\n this._reactiveNodes.delete(nodeId);\n this._reactiveProxies.delete(nodeId);\n this._stateSnapshots.delete(nodeId);\n if (this._stateSnapshotStrs) this._stateSnapshotStrs.delete(nodeId);\n this._pollMeta.delete(nodeId);\n this._lastSnapshotSize.delete(nodeId);\n },\n\n // A component instance or a module scope that is still live.\n _scopeLive(id) {\n return this._instances.has(id) || this._modules.has(id);\n },\n\n _scopeFile(id) {\n const scope = this._instances.get(id) || this._modules.get(id);\n return scope ? scope.file : '';\n },\n\n _enterModule(file) {\n this._moduleFile = file;\n // a module body that throws (or awaits) never reaches _leaveModule\n queueMicrotask(() => {\n if (this._moduleFile === file) this._moduleFile = null;\n });\n },\n\n _leaveModule() {\n this._moduleFile = null;\n },\n\n trackModuleSignal(kind, target, name) {\n const file = this._moduleFile;\n let id = this._moduleByFile.get(file);\n if (id === undefined) {\n id = this._nextId++;\n const base = file.split('/').pop() || file;\n this._modules.set(id, { id, file, name: base, kind: 'module' });\n this._moduleByFile.set(file, id);\n }\n if (kind === 'proxy') this.trackProxy(target, name, id, null);\n else if (kind === 'derived') this.trackDerived(target, name, id, null);\n else this.trackState(target, name, id, null);\n },\n\n // A node is live while its signal is reachable, its component is mounted,\n // and the effect it was created in (an {#each} item, an effect run) has\n // not been destroyed. Svelte nulls effect.fn when it destroys an effect\n // (internal/client destroy_effect), which is checked instead of the\n // internal DESTROYED flag value. Returns the signal or null.\n _liveSignal(nodeId) {\n const entry = this._reactiveNodes.get(nodeId);\n if (!entry) return null;\n const signal = entry.signal.deref();\n if (signal && this._scopeLive(entry.meta.componentId) && this._ownerAlive(entry)) {\n if (!entry.effect) return signal;\n // a bound effect is destroyed when Svelte clears its callback\n if (signal.fn !== null) return signal;\n }\n this._forgetNode(nodeId);\n return null;\n },\n\n _ownerAlive(entry) {\n if (!entry.owner) return true;\n const owner = entry.owner.deref();\n return !!owner && owner.fn !== null;\n },\n\n _sweepAdd(nodeId) {\n if (this._sweepIndex.has(nodeId)) return;\n this._sweepIndex.set(nodeId, this._sweepIds.length);\n this._sweepIds.push(nodeId);\n },\n\n _sweepRemove(nodeId) {\n const i = this._sweepIndex.get(nodeId);\n if (i === undefined) return;\n const ids = this._sweepIds;\n const last = ids.pop();\n this._sweepIndex.delete(nodeId);\n if (i < ids.length) {\n ids[i] = last;\n this._sweepIndex.set(last, i);\n }\n },\n\n // Liveness of up to `max` nodes, continuing from a cursor (each poll\n // tick): nodes of destroyed {#each} items / effect runs leave the counts\n // and the graph without waiting for a graph request or GC.\n _sweepNodes(max) {\n const ids = this._sweepIds;\n for (let n = 0; n < max && ids.length > 0; n++) {\n if (this._sweepCursor >= ids.length) this._sweepCursor = 0;\n const nodeId = ids[this._sweepCursor];\n // a forgotten node's slot is refilled by the last id: re-check it\n if (this._liveSignal(nodeId)) this._sweepCursor++;\n }\n },\n\n // Unique node id for a signal: componentId:name, or componentId:name#k\n // when this instance already holds a live node of that name for another\n // signal. The same signal (re-tagged) keeps its id.\n _nodeIdFor(identity, byIdentity, componentId, name) {\n const known = byIdentity.get(identity);\n if (known !== undefined && this._reactiveNodes.has(known)) return known;\n const base = componentId + ':' + name;\n if (!this._reactiveNodes.has(base) || !this._liveSignal(base)) return base;\n let seq = this._nameSeq.get(componentId);\n if (!seq) {\n seq = new Map();\n this._nameSeq.set(componentId, seq);\n }\n let k = seq.get(name) || 1;\n let id;\n do {\n k++;\n id = base + '#' + k;\n } while (this._reactiveNodes.has(id) && this._liveSignal(id));\n seq.set(name, k);\n return id;\n },\n\n _registerNode(nodeId, componentId, type, name, signalRef, owner, extra) {\n this._trackNodeCount(nodeId, componentId, type);\n const entry = {\n signal: signalRef,\n owner: owner ? new WeakRef(owner) : null,\n meta: { id: nodeId, type, name, componentId, componentFile: this._scopeFile(componentId) },\n };\n if (extra) Object.assign(entry, extra);\n this._reactiveNodes.set(nodeId, entry);\n this._indexNode(nodeId, componentId);\n this._sweepAdd(nodeId);\n return entry;\n },\n\n _indexNode(nodeId, componentId) {\n let set = this._nodesByComponent.get(componentId);\n if (!set) {\n set = new Set();\n this._nodesByComponent.set(componentId, set);\n }\n set.add(nodeId);\n },\n\n _pollAdd(nodeId) {\n if (this._pollIndex.has(nodeId)) return;\n if (!this._stateSnapshots.has(nodeId)) this._unseeded.add(nodeId);\n this._pollIndex.set(nodeId, this._pollIds.length);\n this._pollIds.push(nodeId);\n },\n\n // O(1): the last id takes the removed slot. If that slot is behind the\n // cursor, the moved id is visited in the next sweep instead of this one\n // (still visited within a bounded number of ticks).\n // Before a track* call replaces/creates nodeId: move the count from the\n // previous registration (same name re-tracked) to the new one.\n _trackNodeCount(nodeId, componentId, type) {\n const prev = this._reactiveNodes.get(nodeId);\n if (prev) this._countNode(prev.meta.componentId, prev.meta.type, -1);\n this._countNode(componentId, type, 1);\n },\n\n _countNode(componentId, type, d) {\n const kind = type === 'derived' || type === 'effect' ? type : 'state';\n let c = this._nodeCounts.get(componentId);\n if (!c) {\n if (d < 0) return;\n c = { state: 0, derived: 0, effect: 0 };\n this._nodeCounts.set(componentId, c);\n }\n c[kind] += d;\n this._nodeTotals[kind] += d;\n if (c.state + c.derived + c.effect <= 0) this._nodeCounts.delete(componentId);\n },\n\n _bucket() {\n const sec = Math.floor(performance.now() / 1000);\n const slot = ((sec % 60) + 60) % 60;\n let b = this._activity[slot];\n if (!b || b.second !== sec) {\n b = { second: sec, sampledMs: 0, rows: new Map() };\n this._activity[slot] = b;\n }\n return b;\n },\n\n _activityRow(componentId) {\n const rows = this._bucket().rows;\n let r = rows.get(componentId);\n if (!r) {\n r = { changes: 0, renders: 0, renderMs: 0 };\n rows.set(componentId, r);\n }\n return r;\n },\n\n _pollRemove(nodeId) {\n const i = this._pollIndex.get(nodeId);\n if (i === undefined) return;\n this._unseeded.delete(nodeId);\n const ids = this._pollIds;\n const last = ids.pop();\n this._pollIndex.delete(nodeId);\n if (i < ids.length) {\n ids[i] = last;\n this._pollIndex.set(last, i);\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._cleanupComponent(childId);\n }\n },\n\n // Full-list pushes are throttled, not debounced: a pending timer is not\n // reset, so continuous churn still produces updates (a debounce would\n // starve), and the interval adapts to the last push's cost so a 10 000+\n // instance app can't spend its main thread serializing (review P3):\n // max(min, 20 x last send ms, 1 s when the last payload exceeded ~1 MB).\n _sendInterval(cost, min) {\n return Math.max(min, 20 * cost.ms, cost.bytes > 1000000 ? 1000 : 0);\n },\n\n _scheduleUpdate() {\n if (!this._active || this._debounceTimer) return;\n this._debounceTimer = setTimeout(() => {\n this._debounceTimer = null;\n this._sendUpdate();\n }, this._sendInterval(this._componentsCost, 100));\n },\n\n // components push (§6.5). Full form { epoch, components } replaces the\n // server's tree for this epoch: sent on activation, when the server does\n // not advertise componentDeltas, as a periodic checkpoint, or when the\n // delta would be larger than the tree. Otherwise the delta form\n // { epoch, added, removed } (removed ids first, ≤ 2 000 entries per\n // message, added in registration order = parents first).\n _sendUpdate(forceFull) {\n // Nobody watching: keep the registry, skip the O(N) payload. A full\n // snapshot is sent on activation (§6.3).\n if (!this._active) return;\n const t0 = performance.now();\n const CHUNK = 2000;\n const CHECKPOINT_MS = 30000;\n const pending = this._pendingAdded.size + this._pendingRemoved.size;\n const full =\n forceFull ||\n !this._componentDeltas ||\n t0 - this._lastFullAt >= CHECKPOINT_MS ||\n pending > this._instances.size;\n const toEntry = (instance) => ({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n let bytes = 0;\n const msgs = [];\n if (full) {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push(toEntry(instance));\n bytes += instance.file.length + instance.name.length + 72;\n }\n }\n msgs.push({ epoch: this._epoch, reset: true, components });\n this._lastFullAt = t0;\n } else if (pending > 0) {\n const removed = [...this._pendingRemoved];\n const added = [];\n for (const id of this._pendingAdded) {\n const instance = this._instances.get(id);\n if (instance && instance.mounted) {\n added.push(toEntry(instance));\n bytes += instance.file.length + instance.name.length + 72;\n }\n }\n bytes += removed.length * 8;\n for (let r = 0; r < removed.length; r += CHUNK) {\n msgs.push({ epoch: this._epoch, added: [], removed: removed.slice(r, r + CHUNK) });\n }\n for (let a = 0; a < added.length; a += CHUNK) {\n msgs.push({ epoch: this._epoch, added: added.slice(a, a + CHUNK), removed: [] });\n }\n }\n this._pendingAdded.clear();\n this._pendingRemoved.clear();\n if (import.meta.hot) {\n for (const msg of msgs) import.meta.hot.send('svelte-devtools:components', msg);\n }\n this._componentsCost = { ms: performance.now() - t0, bytes };\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._activityRow(id).renders++;\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 this._activityRow(id).renderMs += duration;\n }\n },\n\n // Profiles push (review P-1 Option A; contract in\n // docs/performance-status.md). Throttled like components, but the floor\n // grows with the payload: every push carries the whole retained map, so\n // a large app coalesces bursts instead of resending ~1 MB twice a second.\n // Hard cap 2 s (contract R6): a slow serialization must not stretch the\n // latency beyond it.\n _profileInterval() {\n const cost = this._profilesCost;\n return Math.min(\n 2000,\n Math.max(500, 20 * cost.ms, cost.bytes > 1000000 ? 2000 : cost.bytes > 250000 ? 1000 : 0),\n );\n },\n\n _scheduleProfileUpdate() {\n if (!this._active || this._profileDebounceTimer) return;\n this._profileDebounceTimer = setTimeout(() => {\n this._profileDebounceTimer = null;\n this._sendProfileUpdate();\n }, this._profileInterval());\n },\n\n // Also the forced path (activation / resync): a pending throttled push is\n // cancelled so the same state is not sent twice.\n _sendProfileUpdate() {\n if (this._profileDebounceTimer) {\n clearTimeout(this._profileDebounceTimer);\n this._profileDebounceTimer = null;\n }\n if (!this._active) return;\n const t0 = performance.now();\n const all = Array.from(this._profiles.values());\n // Exactly what the collector retains: its tail, in the same order.\n const profiles = all.length > this._profileCap ? all.slice(all.length - this._profileCap) : all;\n if (import.meta.hot) {\n // total = all retained profiles; the message carries the newest-mounted\n // tail (§6.7 J IA1 capture policy 'newest-mounted').\n import.meta.hot.send('svelte-devtools:profiles', { epoch: this._epoch, profiles, total: all.length });\n }\n // Payload size from a small sample (entries differ mainly by file path).\n const k = Math.min(8, profiles.length);\n let sampled = 0;\n for (let i = 0; i < k; i++) sampled += JSON.stringify(profiles[i]).length;\n const bytes = k > 0 ? Math.round((sampled / k) * profiles.length) : 0;\n this._profilesCost = { ms: performance.now() - t0, bytes };\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 // `owner` (optional): the effect active when the signal was created; the\n // node dies with it (an {#each} item's {@const}, a class instance created\n // in an effect run).\n trackState(signal, name, componentId, owner) {\n const nodeId = this._nodeIdFor(signal, this._idBySignal, componentId, name);\n this._registerNode(nodeId, componentId, 'state', name, new WeakRef(signal), owner);\n this._idBySignal.set(signal, nodeId);\n this._nameProxyRoot(nodeId, name, componentId);\n this._pollAdd(nodeId);\n },\n\n trackProxy(proxy, name, componentId, owner) {\n const nodeId = this._nodeIdFor(proxy, this._idByProxy, componentId, name);\n this._reactiveProxies.set(nodeId, new WeakRef(proxy));\n // The proxy itself stays weakly held (_reactiveProxies). The marker is\n // held strongly so the node is not GC-dropped while the component lives;\n // _cleanupComponent removes it on unmount.\n const marker = { v: '(proxy)', _isProxy: true };\n this._registerNode(nodeId, componentId, 'state', name, { deref: () => marker }, owner);\n this._idBySignal.set(marker, nodeId);\n this._idByProxy.set(proxy, nodeId);\n this._nameProxyRoot(nodeId, name, componentId);\n this._pollAdd(nodeId);\n },\n\n trackDerived(signal, name, componentId, owner) {\n const nodeId = this._nodeIdFor(signal, this._idBySignal, componentId, name);\n this._registerNode(nodeId, componentId, 'derived', name, new WeakRef(signal), owner);\n this._idBySignal.set(signal, nodeId);\n },\n\n // A user effect object (already created). Kept for callers that have one.\n trackEffect(effect, name, componentId) {\n const nodeId = componentId + ':' + name;\n const target = effect || { v: undefined, _isEffect: true };\n this._registerNode(nodeId, componentId, 'effect', name, effect ? new WeakRef(target) : { deref: () => target });\n this._idBySignal.set(target, nodeId);\n return effect;\n },\n\n // $effect / $effect.pre being created now (the wrapper). The effect object\n // may not exist yet (deferred to mount): the node holds a placeholder\n // until bindEffect() is called on the effect's first run. kind: 'effect'\n // | 'effect_pre'; names are effect_1, effect_pre_1, ... per component.\n trackUserEffect(kind, componentId, owner) {\n let seq = this._effectSeq.get(componentId);\n if (!seq) {\n seq = { effect: 0, effect_pre: 0 };\n this._effectSeq.set(componentId, seq);\n }\n const name = kind + '_' + ++seq[kind];\n const nodeId = componentId + ':' + name;\n const placeholder = { v: undefined, _isEffect: true };\n this._registerNode(nodeId, componentId, 'effect', name, { deref: () => placeholder }, owner);\n return nodeId;\n },\n\n bindEffect(nodeId, effect) {\n const entry = nodeId !== null && this._reactiveNodes.get(nodeId);\n if (!entry || !effect || typeof effect !== 'object') return;\n entry.signal = new WeakRef(effect);\n entry.effect = true;\n this._idBySignal.set(effect, nodeId);\n },\n\n // The component's mount hook effect (the wrapper's, created at pop): its\n // parent is the effect the component's markup effects were created in.\n setComponentEffect(id, effect) {\n const instance = this._instances.get(id);\n if (instance) instance.hook = new WeakRef(effect);\n },\n\n // Markup effects of a component: the effects of its template\n // (template_effect, {#if}/{#each}/... blocks, <svelte:head>), i.e. the\n // effects under the hook's parent created in the component's context\n // (effect.ctx), without descending into other components or the\n // tracked $effect nodes. Svelte links child effects via first/next.\n _markupEffects(id) {\n const instance = this._instances.get(id);\n const hook = instance && instance.hook && instance.hook.deref();\n if (!hook || !hook.ctx || !hook.parent) return [];\n const ctx = hook.ctx;\n const out = [];\n const stack = [];\n for (let e = hook.parent.first; e; e = e.next) stack.push(e);\n while (stack.length) {\n const e = stack.pop();\n if (e === hook || e.ctx !== ctx) continue;\n if (e.deps && !this._idBySignal.has(e)) out.push(e);\n for (let c = e.first; c; c = c.next) stack.push(c);\n }\n return out;\n },\n\n // The tracked node a proxy's property source belongs to. Svelte (dev)\n // labels a proxy's sources after the proxy's path ('todos[0].text',\n // 'cart.items.length', 'todos version'); the root name is a $state node\n // name. Many live nodes may share the name (one per instance), so the\n // candidates are looked up per scope: the reader's component, then its\n // ancestors (props flow down), then module scopes, then all — each\n // narrowed by the source's value at that path (identity for objects).\n // Cost O(label + ancestry depth) for the common cases. null when\n // unresolved.\n _sourceOwner(source, readerCid, liveSignal) {\n const label = source.label;\n if (typeof label !== 'string' || this._proxyNames.size === 0) return null;\n let named = null;\n let rest = '';\n for (let i = label.length; i > 0; i--) {\n const ch = i === label.length ? '' : label[i];\n if (ch !== '' && ch !== '.' && ch !== '[' && ch !== ' ') continue;\n named = this._proxyNames.get(label.slice(0, i));\n if (named) {\n rest = label.slice(i);\n break;\n }\n }\n if (!named) return null;\n let path;\n // live candidates whose value at the source's path is the source's\n // value; without a property path (' version'), all live candidates\n const fits = (ids) => {\n const live = [...ids].filter((id) => liveSignal(id));\n if (live.length === 0) return live;\n if (path === undefined) path = this._labelPath(rest);\n if (!path) return live;\n const v = source.v;\n return live.filter((id) => {\n const at = this._valueAt(this._rootValue(id), path);\n return at !== __NO_VALUE && (Object.is(at, v) || (typeof v === 'symbol' && at === undefined));\n });\n };\n const scopes = [];\n for (let cid = readerCid; cid !== null && cid !== undefined; ) {\n scopes.push(cid);\n const instance = this._instances.get(cid);\n cid = instance ? instance.parentId : null;\n }\n for (const cid of named.keys()) if (this._modules.has(cid)) scopes.push(cid);\n for (const cid of scopes) {\n const ids = named.get(cid);\n const found = ids ? fits(ids) : [];\n if (found.length > 0) return found[0];\n }\n // not in the reader's ancestry or a module: any matching node, else the\n // nearest live one by name\n const all = [];\n for (const ids of named.values()) for (const id of ids) all.push(id);\n const found = fits(all);\n if (found.length > 0) return found[0];\n for (const cid of scopes) for (const id of named.get(cid) || []) if (liveSignal(id)) return id;\n return all.find((id) => liveSignal(id)) || null;\n },\n\n // '.items[0].text' -> ['items', '0', 'text']; null for ' version' and\n // anything else that is not a property path.\n _labelPath(rest) {\n const path = [];\n const re = /\\.([A-Za-z_$][\\w$]*)|\\[(\\d+)\\]|\\['((?:[^'\\\\]|\\\\.)*)'\\]/y;\n let m;\n while (re.lastIndex < rest.length && (m = re.exec(rest))) path.push(m[1] ?? m[2] ?? m[3]);\n return re.lastIndex === rest.length || rest.length === 0 ? path : null;\n },\n\n // Own-property walk (getOwnPropertyDescriptor does not create sources on\n // a Svelte proxy, unlike reading a missing property).\n _valueAt(obj, path) {\n let v = obj;\n for (const key of path) {\n if (v === null || (typeof v !== 'object' && typeof v !== 'function')) return __NO_VALUE;\n let d;\n try { d = Object.getOwnPropertyDescriptor(v, key); } catch { return __NO_VALUE; }\n if (!d) return __NO_VALUE;\n v = 'value' in d ? d.value : __NO_VALUE;\n if (v === __NO_VALUE) return v;\n }\n return v;\n },\n\n _nameProxyRoot(nodeId, name, componentId) {\n let named = this._proxyNames.get(name);\n if (!named) {\n named = new Map();\n this._proxyNames.set(name, named);\n }\n let inScope = named.get(componentId);\n if (!inScope) {\n inScope = new Set();\n named.set(componentId, inScope);\n }\n inScope.add(nodeId);\n },\n\n // The proxy a $state node holds now (tag_proxy: the proxy itself; a\n // state signal: its current value), or undefined.\n _rootValue(nodeId) {\n const proxy = this._reactiveProxies.get(nodeId);\n if (proxy) return proxy.deref();\n const signal = this._reactiveNodes.get(nodeId)?.signal.deref();\n return signal ? signal.v : undefined;\n },\n\n // A component whose init threw (the wrapper unwinds its stack): it never\n // mounted and never will, so it leaves the tree and the init stack.\n abortInit(id) {\n const idx = this._stack.indexOf(id);\n if (idx !== -1) this._stack.splice(idx, 1);\n const instance = this._instances.get(id);\n if (!instance) return;\n if (instance.parentId !== null) {\n const parent = this._instances.get(instance.parentId);\n if (parent) parent.children.delete(id);\n }\n this._removeChildren(id);\n this._cleanupComponent(id);\n this._scheduleUpdate();\n },\n\n _graphNode(nodeId, entry, signal) {\n const node = { ...entry.meta };\n if (signal._isProxy) {\n const proxy = this._reactiveProxies.get(nodeId)?.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 === 'derived' && typeof signal.v === 'symbol') {\n // Svelte computes a derived lazily: until something reads it, v is\n // the UNINITIALIZED marker (a symbol)\n node.unevaluated = true;\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 return node;\n },\n\n // Reactive graph (§6.7 A/D). Caps are applied while building: past\n // maxNodes no node objects are created and only edges whose endpoints\n // are both included are emitted (the rest are counted in edgesOmitted).\n // - scoped (componentId): that component's nodes plus the tracked nodes\n // directly connected to them (via deps / reactions through untracked\n // intermediates). Dependencies are resolved through _idBySignal, so\n // the cost is O(scope + neighbours), not O(all nodes). The walk is\n // completed even past the cap, so total.nodes/edges are exact.\n // - global: nodes in registration order up to maxNodes ('global-head'),\n // with their incoming dependency edges. Nodes past the cap are not\n // dereferenced; total.nodes is the registered count (may include\n // nodes whose signal was already collected, nodesKind 'registered')\n // and total.edges is null when the walk stopped early (not guessed).\n // Template consumers: the compiler turns every dynamic part of the markup\n // ({expr}, attributes, {#if}/{#each}/{#key}/{#await} conditions,\n // <svelte:head>) into effects that are not tracked one by one. A read\n // from such an effect is reported as an edge to the synthetic node\n // '<componentId>:(template)' (type 'template') of the component the\n // effect belongs to (effect.ctx), so a value only the markup reads is\n // not shown without readers.\n getReactiveGraph(componentId, caps) {\n const scoped = componentId !== undefined && componentId !== null;\n const maxNodes = this._graphCap(caps && caps.maxNodes, 5000);\n const maxEdges = this._graphCap(caps && caps.maxEdges, 20000);\n const live = new Map();\n // Live signal of a tracked node, or null (forgets dead / unmounted nodes).\n const liveSignal = (nodeId) => {\n if (live.has(nodeId)) return live.get(nodeId);\n const signal = this._liveSignal(nodeId);\n live.set(nodeId, signal);\n return signal;\n };\n const idOf = (dep) => {\n const id = this._idBySignal.get(dep);\n return id !== undefined && liveSignal(id) === dep ? id : null;\n };\n // Synthetic template nodes reached in this build: id -> componentId.\n const templates = new Map();\n // Template node of an untracked effect (no reactions: not a derived),\n // or null when its component is unknown / unmounted.\n const templateOf = (effect) => {\n if (!effect || 'reactions' in effect || !effect.ctx) return null;\n const cid = this._idByContext.get(effect.ctx);\n if (cid === undefined || !this._instances.has(cid)) return null;\n const id = cid + ':(template)';\n templates.set(id, cid);\n return id;\n };\n\n const included = new Set();\n const seen = new Set();\n const edges = [];\n const edgeSet = new Set();\n let edgesOmitted = 0;\n let truncated = false;\n const see = (id) => {\n seen.add(id);\n if (!included.has(id) && included.size < maxNodes) included.add(id);\n return included.has(id);\n };\n const addEdge = (from, to) => {\n const key = from + '>' + to;\n if (from === to || edgeSet.has(key)) return;\n edgeSet.add(key);\n const inFrom = see(from);\n const inTo = see(to);\n if (inFrom && inTo && edges.length < maxEdges) edges.push({ from, to });\n else edgesOmitted++;\n };\n // Walk a signal's neighbours (deps or reactions) through untracked\n // intermediates until tracked nodes are reached. onTemplate (reactions\n // only) receives the template node of each untracked effect reached.\n // On the deps side an untracked leaf source (a proxy's property) is\n // resolved to its proxy's node (readerCid: the reading component).\n // nodeId -> dependencies on signals devtools does not track (created\n // in node_modules, e.g. SvelteKit's page state, or outside any\n // component/module body): reported so such a node is not \"isolated\"\n const untracked = new Map();\n const walk = (start, field, onHit, onTemplate, readerCid, onUntracked) => {\n if (!start) return;\n const visited = new Set();\n const queue = [...start];\n // Index cursor instead of queue.shift(): shift is O(queue) per pop.\n for (let qi = 0; qi < queue.length; qi++) {\n const dep = queue[qi];\n if (!dep || visited.has(dep)) continue;\n visited.add(dep);\n const id = idOf(dep);\n if (id) {\n if (onHit) onHit(id);\n } else if (dep[field]) {\n for (const d of dep[field]) queue.push(d);\n } else if (onTemplate) {\n const t = templateOf(dep);\n if (t) onTemplate(t);\n } else if (field === 'deps' && onHit && !('fn' in dep)) {\n const owner = this._sourceOwner(dep, readerCid, liveSignal);\n if (owner) onHit(owner);\n else if (onUntracked) onUntracked();\n }\n }\n };\n const nodeCid = (nodeId) => this._reactiveNodes.get(nodeId).meta.componentId;\n const countUntracked = (nodeId) => () => untracked.set(nodeId, (untracked.get(nodeId) || 0) + 1);\n // Reads of a component's markup effects: edges into its template node\n // (filter: keep only edges from these node ids, for neighbours).\n const markupEdges = (cid, filter) => {\n const effects = this._markupEffects(cid);\n if (effects.length === 0) return;\n const t = cid + ':(template)';\n for (const effect of effects) {\n walk(effect.deps, 'deps', (id) => {\n if (filter && !filter(id)) return;\n templates.set(t, cid);\n addEdge(id, t);\n }, null, cid);\n }\n };\n\n let totalNodes = 0;\n let totalEdges = null;\n if (scoped) {\n for (const nodeId of [...(this._nodesByComponent.get(componentId) || [])]) {\n const signal = liveSignal(nodeId);\n if (!signal) continue;\n see(nodeId);\n walk(signal.deps, 'deps', (depId) => addEdge(depId, nodeId), null, componentId, countUntracked(nodeId));\n const toReader = (rId) => addEdge(nodeId, rId);\n walk(signal.reactions, 'reactions', toReader, toReader);\n }\n if (this._instances.has(componentId)) markupEdges(componentId, null);\n // Readers in direct children (props): a proxy has no reactions list\n // of its own, so its readers are found from their dependencies.\n const own = (id) => nodeCid(id) === componentId;\n const instance = this._instances.get(componentId);\n for (const child of instance ? instance.children : []) {\n for (const nodeId of [...(this._nodesByComponent.get(child) || [])]) {\n const signal = liveSignal(nodeId);\n if (signal) walk(signal.deps, 'deps', (depId) => own(depId) && addEdge(depId, nodeId), null, child);\n }\n markupEdges(child, own);\n }\n totalNodes = seen.size;\n totalEdges = edgeSet.size;\n truncated = included.size < seen.size || edgesOmitted > 0;\n } else {\n let complete = true;\n for (const nodeId of [...this._reactiveNodes.keys()]) {\n if (included.size >= maxNodes && !included.has(nodeId)) {\n complete = false;\n break;\n }\n const signal = liveSignal(nodeId);\n if (!signal) continue;\n see(nodeId);\n walk(signal.deps, 'deps', (depId) => addEdge(depId, nodeId), null, nodeCid(nodeId), countUntracked(nodeId));\n // tracked readers are found from their own deps; only template\n // readers need the reactions side\n walk(signal.reactions, 'reactions', null, (t) => addEdge(nodeId, t));\n }\n if (complete) for (const cid of this._instances.keys()) markupEdges(cid, null);\n // registered count (+ template nodes reached), minus nodes found\n // dead while building\n totalNodes = this._reactiveNodes.size + templates.size;\n totalEdges = complete ? edgeSet.size : null;\n truncated = !complete || edgesOmitted > 0;\n }\n\n const nodes = [];\n for (const nodeId of included) {\n const cid = templates.get(nodeId);\n if (cid !== undefined) {\n const instance = this._instances.get(cid);\n nodes.push({ id: nodeId, type: 'template', name: '(template)', componentId: cid, componentFile: instance ? instance.file : '' });\n continue;\n }\n const signal = liveSignal(nodeId);\n if (!signal) continue;\n const node = this._graphNode(nodeId, this._reactiveNodes.get(nodeId), signal);\n if (untracked.has(nodeId)) node.untrackedDeps = untracked.get(nodeId);\n nodes.push(node);\n }\n return {\n scope: scoped ? componentId : null,\n nodes,\n edges,\n total: { nodes: totalNodes, nodesKind: 'registered', edges: totalEdges },\n truncated,\n edgesOmitted,\n policy: scoped ? 'scoped' : 'global-head',\n };\n },\n\n _graphCap(value, max) {\n const n = Math.floor(Number(value));\n return Number.isFinite(n) && n >= 1 ? Math.min(n, max) : max;\n },\n\n // Request (§6.7 A + review M1): { requestId?, epoch?, componentId?,\n // maxNodes?, maxEdges? }. A request for another page load (epoch) is not\n // answered by this one; the reply echoes requestId and epoch. An old\n // server sends {} → global graph with the default caps (= its LIMITS).\n sendReactiveGraph(request) {\n const req = request || {};\n if (req.epoch !== undefined && req.epoch !== null && req.epoch !== this._epoch) return;\n const cid = typeof req.componentId === 'number' ? req.componentId : null;\n const graph = this.getReactiveGraph(cid, { maxNodes: req.maxNodes, maxEdges: req.maxEdges });\n graph.epoch = this._epoch;\n // freshness (review): when this answer was built; caches keep it\n graph.computedAt = Date.now();\n if (req.requestId !== undefined) graph.requestId = req.requestId;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-graph', graph);\n }\n },\n\n // Overview aggregate (§6.7 I/J). Built from the activity buckets of the\n // window (whole seconds: the current partial second plus the previous\n // ones, ceil(windowMs / 1000) buckets) — cost O(components active in the\n // window), no graph, no node scan. Rows rank by sampled changes, then\n // renders, then render ms; rows + other = total (components and nodes).\n getReactiveSummary(opts) {\n const o = opts || {};\n const clampInt = (v, lo, hi, d) => {\n const n = Math.floor(Number(v));\n return Number.isFinite(n) ? Math.min(hi, Math.max(lo, n)) : d;\n };\n const topK = clampInt(o.topK, 1, 200, 50);\n const windowMs = clampInt(o.windowMs, 1000, 60000, 10000);\n const nowSec = Math.floor(performance.now() / 1000);\n const agg = new Map();\n let sampledMs = 0;\n for (let k = 0; k < Math.ceil(windowMs / 1000); k++) {\n const sec = nowSec - k;\n const b = this._activity[((sec % 60) + 60) % 60];\n if (!b || b.second !== sec) continue;\n sampledMs += b.sampledMs;\n for (const [cid, r] of b.rows) {\n if (!this._scopeLive(cid)) continue;\n let a = agg.get(cid);\n if (!a) {\n a = { changes: 0, renders: 0, renderMs: 0 };\n agg.set(cid, a);\n }\n a.changes += r.changes;\n a.renders += r.renders;\n a.renderMs += r.renderMs;\n }\n }\n const ranked = [...agg].sort(\n (x, y) =>\n y[1].changes - x[1].changes || y[1].renders - x[1].renders || y[1].renderMs - x[1].renderMs || x[0] - y[0],\n );\n let rowNodes = 0;\n const rows = ranked.slice(0, topK).map(([cid, a]) => {\n const n = this._nodeCounts.get(cid) || { state: 0, derived: 0, effect: 0 };\n rowNodes += n.state + n.derived + n.effect;\n const row = {\n componentId: cid,\n file: this._scopeFile(cid),\n nodes: { state: n.state, derived: n.derived, effect: n.effect },\n changes: a.changes,\n renders: a.renders,\n renderMs: Math.round(a.renderMs * 1000) / 1000,\n };\n // module scope (shared .svelte.js state), not a component instance\n if (this._modules.has(cid)) row.kind = 'module';\n return row;\n });\n const t = this._nodeTotals;\n // module scopes count as rows like components (rows + other = total)\n const scopes = this._instances.size + this._modules.size;\n const until = Date.now();\n return {\n epoch: this._epoch,\n window: { ms: windowMs, since: until - windowMs, until, sampledActiveMs: Math.min(windowMs, sampledMs) },\n policy: 'sampled-200ms',\n coverage: 'component-init',\n components: { total: scopes, withActivity: agg.size },\n rows,\n other: { components: scopes - rows.length, nodes: t.state + t.derived + t.effect - rowNodes },\n truncated: agg.size > topK,\n capabilities: { valueInspection: false, signalHistory: false, writeCause: false },\n // Seed coverage: changes of pending nodes are not observable yet.\n baseline: this._baselineInfo(),\n };\n },\n\n // Request { requestId?, epoch?, topK?, windowMs? } (same correlation as\n // the graph, review M1).\n sendReactiveSummary(request) {\n const req = request || {};\n if (req.epoch !== undefined && req.epoch !== null && req.epoch !== this._epoch) return;\n const summary = this.getReactiveSummary(req);\n if (req.requestId !== undefined) summary.requestId = req.requestId;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-summary', summary);\n }\n },\n\n // --- Phase 3: State Timeline ---\n\n // Current value of a $state node: the signal value, or for tag_proxy\n // nodes (non-reassigned object/array state) the live proxy itself.\n // Returns a sentinel when the value can't be read.\n _readStateValue(nodeId, signal) {\n if (signal._isProxy) {\n const proxy = this._reactiveProxies.get(nodeId)?.deref();\n return proxy === undefined ? __NO_VALUE : proxy;\n }\n const v = signal.v;\n // Svelte's UNINITIALIZED marker is a symbol; functions aren't state data.\n return typeof v === 'symbol' || typeof v === 'function' ? __NO_VALUE : v;\n },\n\n // Runs every 200 ms in the user's app, so it must stay cheap regardless of\n // how much state the app holds (§6.7 D).\n // 1. Hint pass, time-sliced: at most HINT_NODES $state nodes per tick,\n // continuing from a cursor carried across ticks, so a full sweep over\n // n nodes takes ceil(n / HINT_NODES) ticks (_pollSweep.lastMs is the\n // measured sweep time = worst-case detection delay). Per node O(1):\n // primitives are compared directly and recorded at once; objects\n // whose reference or Svelte write version (wv) changed are added to\n // _pollDirty, which persists across ticks until serialized.\n // 2. Serialization work is limited to BUDGET_MS per tick (dirty nodes\n // carry over, nothing is dropped).\n // 3. In-place deep mutations (proxies don't bump an outer wv) are found by\n // time-based re-checks: each object is re-verified no earlier than\n // nextCheckAt = now + max(RECHECK_MS, 50 x its serialization cost),\n // paid from a DEEP_MS-per-tick credit, so the amortized cost is bounded\n // however many large states the app holds.\n // 4. Values serializing above MAX_SNAPSHOT_CHARS are kept as length+hash\n // (not the string) and appear in the timeline as a summary.\n // Writes between two visits of a node are observed as one change (the\n // timeline is a sample, policy 'sampled-200ms'; not counted as dropped).\n _pollStateValues(baseline) {\n const HINT_NODES = 4096;\n const SWEEP_NODES = 2048;\n const BUDGET_MS = 2;\n const DEEP_MS = 0.5;\n const now = performance.now();\n if (!baseline) {\n // nodes of destroyed {#each} items / effect runs leave the counts\n this._sweepNodes(SWEEP_NODES);\n this._deepCredit = Math.min(10 * DEEP_MS, (this._deepCredit || 0) + DEEP_MS);\n // this tick samples 200 ms of wall time (window.sampledActiveMs)\n const bucket = this._bucket();\n bucket.sampledMs = Math.min(1000, bucket.sampledMs + 200);\n }\n const ids = this._pollIds;\n const due = [];\n const limit = Math.min(HINT_NODES, ids.length);\n for (let visited = 0; visited < limit && ids.length > 0; visited++) {\n if (this._pollCursor >= ids.length) {\n this._pollCursor = 0;\n if (!baseline) {\n if (this._pollSweep.startedAt) this._pollSweep.lastMs = now - this._pollSweep.startedAt;\n this._pollSweep.startedAt = now;\n this._pollSweep.nodes = ids.length;\n }\n }\n if (!baseline && !this._pollSweep.startedAt) this._pollSweep.startedAt = now;\n const nodeId = ids[this._pollCursor];\n const entry = this._reactiveNodes.get(nodeId);\n if (!entry || entry.meta.type !== 'state') {\n // re-tracked as another type: drop from the poll list (slot reused)\n this._pollRemove(nodeId);\n continue;\n }\n if (!this._scopeLive(entry.meta.componentId)) {\n this._forgetNode(nodeId); // swaps the last id into this slot\n continue;\n }\n this._pollCursor++;\n const signal = entry.signal.deref();\n const value = signal ? this._readStateValue(nodeId, signal) : __NO_VALUE;\n if (value === __NO_VALUE) {\n // nothing readable (collected, uninitialized, function): not pending\n this._unseeded.delete(nodeId);\n continue;\n }\n if (value === null || typeof value !== 'object') {\n const had = this._stateSnapshots.has(nodeId);\n const prev = this._stateSnapshots.get(nodeId);\n if (had && Object.is(prev, value)) continue;\n // Forget the object key so object -> primitive -> same object again\n // is still recorded as a change.\n if (this._stateSnapshotStrs) this._stateSnapshotStrs.delete(nodeId);\n this._pollDirty.delete(nodeId);\n if (!had) {\n // first observation: seed only\n this._stateSnapshots.set(nodeId, value);\n this._unseeded.delete(nodeId);\n continue;\n }\n this._recordChange(nodeId, entry, prev, value, typeof value === 'string' ? 2 * value.length : 64);\n continue;\n }\n let meta = this._pollMeta.get(nodeId);\n if (!meta) {\n meta = { ref: undefined, wv: undefined, nextCheckAt: 0 };\n this._pollMeta.set(nodeId, meta);\n }\n if (meta.ref !== value || meta.wv !== signal.wv) this._pollDirty.add(nodeId);\n else if (now >= meta.nextCheckAt) due.push(nodeId);\n }\n const start = performance.now();\n for (const nodeId of this._pollDirty) {\n if (performance.now() - start > BUDGET_MS) return;\n this._pollDirty.delete(nodeId);\n this._serializeNode(nodeId, false);\n }\n for (const nodeId of due) {\n if (this._deepCredit <= 0 || performance.now() - start > BUDGET_MS) return;\n this._serializeNode(nodeId, true);\n }\n },\n\n // Activation (late consumer): nodes registered while inactive have no\n // seed yet, so a write before their first visit could not be seen as a\n // change. The first slice is seeded at activation instead of 200 ms later:\n // ONE normal pass (<= HINT_NODES nodes, the per-tick serialization budget;\n // the rest follow on the usual ticks). Seeds record nothing; readiness is\n // disclosed in the summary as baseline { complete, pendingNodes }.\n _pollBaseline() {\n this._baselining = true;\n try {\n this._pollStateValues(true);\n } catch { /* never break activation */ } finally {\n this._baselining = false;\n }\n },\n\n _serializeNode(nodeId, deep) {\n const MAX_SNAPSHOT_CHARS = 32768;\n const RECHECK_MS = 1000;\n const entry = this._reactiveNodes.get(nodeId);\n const meta = this._pollMeta.get(nodeId);\n if (!entry || !meta) return;\n const signal = entry.signal.deref();\n if (!signal) return;\n const value = this._readStateValue(nodeId, signal);\n if (value === __NO_VALUE || value === null || typeof value !== 'object') return;\n try {\n const t0 = performance.now();\n const str = JSON.stringify(value);\n const cost = performance.now() - t0;\n if (deep) this._deepCredit -= cost;\n meta.ref = value;\n meta.wv = signal.wv;\n meta.nextCheckAt = t0 + Math.max(RECHECK_MS, 50 * cost);\n // Small values keep the string (exact compare); large ones only a\n // length+hash so a 5 MB array doesn't pin 5 MB per node.\n const key = str === undefined ? '' : str.length > MAX_SNAPSHOT_CHARS ? str.length + ':' + __hash(str) : str;\n if (!this._stateSnapshotStrs) this._stateSnapshotStrs = new Map();\n const hadKey = this._stateSnapshotStrs.has(nodeId);\n if (hadKey && this._stateSnapshotStrs.get(nodeId) === key) return;\n this._stateSnapshotStrs.set(nodeId, key);\n const tooLarge = str !== undefined && str.length > MAX_SNAPSHOT_CHARS;\n const snapshot =\n str === undefined\n ? null\n : tooLarge\n ? '(object: ' + str.length + ' chars, too large to snapshot)'\n : JSON.parse(str);\n const size = typeof snapshot === 'string' ? 64 : str.length;\n if (!this._stateSnapshots.has(nodeId)) {\n // first observation: seed only (no entry, no counters)\n this._stateSnapshots.set(nodeId, snapshot);\n this._lastSnapshotSize.set(nodeId, size);\n this._unseeded.delete(nodeId);\n return;\n }\n if (tooLarge) this._valueTooLarge++;\n const prev = this._stateSnapshots.get(nodeId);\n this._recordChange(nodeId, entry, prev, snapshot, size + (this._lastSnapshotSize.get(nodeId) || 0));\n this._lastSnapshotSize.set(nodeId, size);\n } catch {\n // non-serializable (cycles, BigInt): never observable, so not pending\n this._unseeded.delete(nodeId);\n }\n },\n\n _recordChange(nodeId, entry, oldValue, newValue, approxBytes) {\n const change = {\n id: nodeId,\n name: entry.meta.name,\n componentFile: entry.meta.componentFile,\n oldValue,\n newValue,\n timestamp: Date.now(),\n };\n const bytes = (approxBytes || 64) + 192;\n // seq stays runtime-internal: the server assigns its own seq (§6.4).\n this._entryInfo.set(change, { seq: ++this._timelineSeq, bytes });\n this._stateTimeline.push(change);\n this._timelineBytes += bytes;\n // sampled change count (§6.7 J IA2): independent of the ring\n this._activityRow(entry.meta.componentId).changes++;\n this._stateSnapshots.set(nodeId, newValue);\n // The ring (_trimTimeline) is applied in bulk, not with one splice per\n // change (a poll tick can record thousands): before every read (each\n // push — with a hot channel and an active consumer, at least every 200\n // changes — the full snapshot, getStateTimeline() and clear), and here\n // once more than __TIMELINE_TRIM_AT entries are held or the byte budget\n // is exceeded, which bounds memory when nothing reads or pushes.\n if (this._stateTimeline.length > __TIMELINE_TRIM_AT || this._timelineBytes > __TIMELINE_BYTES) {\n this._trimTimeline();\n }\n this._scheduleTimelineUpdate();\n },\n\n // Ring: at most 500 entries and __TIMELINE_BYTES (the newest entry is\n // always kept). Removed entries that were never pushed are disclosed\n // as dropped (§6.7 C); pushed ones are already on the server.\n _trimTimeline() {\n const all = this._stateTimeline;\n let cut = 0;\n let bytesLeft = this._timelineBytes;\n while (cut < all.length - 1 && (all.length - cut > 500 || bytesLeft > __TIMELINE_BYTES)) {\n const info = this._entryInfo.get(all[cut]);\n const reason = all.length - cut > 500 ? 'runtime-count' : 'runtime-bytes';\n if (info.seq > this._pushedSeq) this._timelineDropped[reason]++;\n bytesLeft -= info.bytes;\n cut++;\n }\n if (cut > 0) {\n all.splice(0, cut);\n this._timelineBytes = bytesLeft;\n }\n },\n\n // Push (§6.7 C): a throttle, not a debounce — a pending timer is never\n // re-armed, so unsent entries leave within 300 ms of the first one even\n // under continuous change — plus an immediate flush once 200 entries\n // (one message) are unsent, since one poll tick can record thousands.\n _scheduleTimelineUpdate() {\n if (!this._active || this._baselining) return;\n if (this._timelineSeq - this._pushedSeq >= 200) {\n this._flushTimeline();\n return;\n }\n if (this._timelineDebounceTimer) return;\n this._timelineDebounceTimer = setTimeout(() => {\n this._timelineDebounceTimer = null;\n this._flushTimeline();\n }, 300);\n },\n\n _flushTimeline() {\n if (this._timelineDebounceTimer) {\n clearTimeout(this._timelineDebounceTimer);\n this._timelineDebounceTimer = null;\n }\n if (!import.meta.hot) return;\n for (const msg of this._timelineDeltas()) {\n import.meta.hot.send('svelte-devtools:state-timeline', msg);\n }\n },\n\n // Disclosure counters since the previous push, attached to (and reset\n // by) the next message: dropped [{ reason, count }] and valueTooLarge.\n _takeTimelineDisclosure(msg) {\n msg.baseline = this._baselineInfo();\n this._sentPendingNodes = msg.baseline.pendingNodes;\n const dropped = [];\n for (const reason of ['runtime-count', 'runtime-bytes']) {\n const count = this._timelineDropped[reason];\n if (count > 0) dropped.push({ reason, count });\n this._timelineDropped[reason] = 0;\n }\n if (dropped.length) msg.dropped = dropped;\n if (this._valueTooLarge > 0) msg.valueTooLarge = this._valueTooLarge;\n this._valueTooLarge = 0;\n return msg;\n },\n\n // Seed coverage (review B-2), on every state-timeline message and the\n // summary: pendingNodes = polled $state nodes without a first sample;\n // their writes are not observable yet.\n _baselineInfo() {\n return { complete: this._unseeded.size === 0, pendingNodes: this._unseeded.size };\n },\n\n _hasTimelineDisclosure() {\n return (\n this._sentPendingNodes !== this._unseeded.size ||\n this._timelineDropped['runtime-count'] > 0 ||\n this._timelineDropped['runtime-bytes'] > 0 ||\n this._valueTooLarge > 0\n );\n },\n\n // Delta push (docs/devframe-migration.md §6.4): only entries recorded\n // since the previous push, ≤ 200 per message, tagged with a per-page-load\n // epoch; reset after clearStateTimeline(). A message with no changes is\n // still sent when there is something to disclose (all unsent dropped).\n _timelineDeltas() {\n this._trimTimeline();\n const all = this._stateTimeline;\n let start = all.length;\n while (start > 0 && this._entryInfo.get(all[start - 1]).seq > this._pushedSeq) start--;\n const pending = all.slice(start);\n const msgs = [];\n const first = this._timelineReset || this._hasTimelineDisclosure();\n for (let i = 0; i < pending.length || (first && i === 0); i += 200) {\n const msg = { epoch: this._epoch, changes: pending.slice(i, i + 200) };\n if (i === 0 && this._timelineReset) msg.reset = true;\n if (i === 0) this._takeTimelineDisclosure(msg);\n else msg.baseline = this._baselineInfo();\n msgs.push(msg);\n }\n this._timelineReset = false;\n this._pushedSeq = this._timelineSeq;\n return msgs;\n },\n\n // Full buffer (reset: true) — reply to request-state-timeline and the\n // activation snapshot. Newest entries first up to __TIMELINE_BYTES, the\n // server's own buffer budget, so nothing is sent that it would drop.\n _timelineFull() {\n this._trimTimeline();\n const all = this._stateTimeline;\n let start = all.length;\n let bytes = 0;\n while (start > 0) {\n const size = this._entryInfo.get(all[start - 1]).bytes;\n if (bytes + size > __TIMELINE_BYTES && start < all.length) break;\n bytes += size;\n start--;\n }\n this._timelineReset = false;\n this._pushedSeq = this._timelineSeq;\n return this._takeTimelineDisclosure({ epoch: this._epoch, changes: all.slice(start), reset: true });\n },\n\n getStateTimeline() {\n this._trimTimeline();\n return this._stateTimeline;\n },\n\n clearStateTimeline() {\n // Ring first: unsent entries past the cap are disclosed as dropped,\n // exactly as if they had been removed when recorded.\n this._trimTimeline();\n this._stateTimeline = [];\n this._timelineBytes = 0;\n this._pushedSeq = this._timelineSeq;\n this._timelineReset = true;\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(gen) {\n if (gen !== this._fpsGen) return;\n this._fpsFrameTimes.push(performance.now());\n requestAnimationFrame(() => this._fpsLoop(gen));\n },\n\n _isVisible() {\n return typeof document === 'undefined' || document.visibilityState !== 'hidden';\n },\n\n _setSubscription(data) {\n const wasActive = this._active;\n this._active = !!(data && data.active);\n this._componentDeltas = !!(data && data.componentDeltas);\n if (!this._active) {\n this._pendingAdded.clear();\n this._pendingRemoved.clear();\n }\n // resync: the server holds no full base for this epoch (§6.5, review\n // C1), or we reconnected to a possibly restarted server.\n const resync = this._resync || !!(data && data.resync);\n this._resync = false;\n if (this._active && !wasActive) this._pollBaseline();\n if (this._active && (!wasActive || resync)) {\n // Collector state may be stale or empty (first activation, resync, or a\n // reconnect to a possibly restarted server): resend everything once, in\n // the order components -> profiles -> timeline(reset: true); the\n // reset push marks the activation snapshot complete (D4).\n this._sendUpdate(true);\n this._sendProfileUpdate();\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:state-timeline', this._timelineFull());\n }\n }\n this._syncSampling();\n },\n\n // Sampling (state poll + FPS) runs only while active AND the app tab is\n // visible.\n _syncSampling() {\n const want = this._active && this._isVisible();\n if (want && !this._sampling) {\n this._fpsFrameTimes = [];\n const gen = ++this._fpsGen;\n this._sampling = {\n poll: setInterval(() => {\n this._pollStateValues();\n // seed progress changed: push it even without changes (B-2)\n if (this._sentPendingNodes !== this._unseeded.size) this._scheduleTimelineUpdate();\n }, 200),\n fps: setInterval(() => { this._sampleFps(); }, 500),\n };\n requestAnimationFrame(() => this._fpsLoop(gen));\n } else if (!want && this._sampling) {\n clearInterval(this._sampling.poll);\n clearInterval(this._sampling.fps);\n this._sampling = null;\n this._fpsGen++;\n this._fpsFrameTimes = [];\n }\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 // State polling and FPS sampling start on the server's subscription\n // message (§6.3) and pause while the app tab is hidden.\n if (typeof document !== 'undefined' && document.addEventListener) {\n document.addEventListener('visibilitychange', () => __SVELTE_DT._syncSampling());\n }\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 // Pull requests are answered regardless of the subscription state.\n if (import.meta.hot) {\n import.meta.hot.on('svelte-devtools:subscription', (data) => {\n __SVELTE_DT._setSubscription(data);\n });\n import.meta.hot.on('vite:ws:connect', () => {\n // The server may have restarted with an empty collector: the reply to\n // runtime-ready is treated as a fresh activation (full snapshot).\n __SVELTE_DT._resync = true;\n import.meta.hot.send('svelte-devtools:runtime-ready', {});\n });\n import.meta.hot.send('svelte-devtools:runtime-ready', {});\n import.meta.hot.on('svelte-devtools:request-reactive-graph', (data) => {\n __SVELTE_DT.sendReactiveGraph(data);\n });\n import.meta.hot.on('svelte-devtools:request-reactive-summary', (data) => {\n __SVELTE_DT.sendReactiveSummary(data);\n });\n import.meta.hot.on('svelte-devtools:request-state-timeline', () => {\n import.meta.hot.send('svelte-devtools:state-timeline', __SVELTE_DT._timelineFull());\n });\n import.meta.hot.on('svelte-devtools:clear-state-timeline', () => {\n __SVELTE_DT.clearStateTimeline();\n });\n }\n}\n", C = {
214
214
  liveComponents: 5e4,
215
215
  renderProfiles: 5e3,
216
216
  stateTimeline: 500,
@@ -220,7 +220,7 @@ var S = "virtual:svelte-devtools-runtime", ge = "\0virtual:svelte-devtools-runti
220
220
  runtimeErrors: 200,
221
221
  loadProfiles: 200,
222
222
  compilerWarnings: 500
223
- }, be = 1e3, xe = Date.now() * 1e3, w = () => ++xe, T = "(legacy)", E = {
223
+ }, w = 1e3, be = Date.now() * 1e3, T = () => ++be, E = "(legacy)", D = {
224
224
  components: "svelte-devtools:components",
225
225
  profiles: "svelte-devtools:profiles",
226
226
  stateTimeline: "svelte-devtools:state-timeline",
@@ -234,19 +234,19 @@ var S = "virtual:svelte-devtools-runtime", ge = "\0virtual:svelte-devtools-runti
234
234
  clearStateTimeline: "svelte-devtools:clear-state-timeline",
235
235
  runtimeReady: "svelte-devtools:runtime-ready",
236
236
  subscription: "svelte-devtools:subscription"
237
- }, Se = 32, D = {
237
+ }, xe = 32, O = {
238
238
  topK: 50,
239
239
  maxTopK: 200,
240
240
  windowMs: 1e4,
241
241
  minWindowMs: 1e3,
242
242
  maxWindowMs: 6e4
243
- }, O = [
243
+ }, k = [
244
244
  "runtime-count",
245
245
  "runtime-bytes",
246
246
  "server-count",
247
247
  "server-bytes"
248
248
  ];
249
- function k(e) {
249
+ function Se(e) {
250
250
  let t = e;
251
251
  return !!t && typeof t.complete == "boolean" && Number.isInteger(t.pendingNodes) && t.pendingNodes >= 0;
252
252
  }
@@ -276,22 +276,22 @@ var we = class {
276
276
  runtimeErrors = [];
277
277
  fpsSamples = [];
278
278
  versions = {
279
- components: w(),
280
- renderProfiles: w(),
281
- loadProfiles: w(),
282
- stateTimeline: w(),
283
- reactiveGraph: w(),
284
- errors: w(),
285
- fps: w()
279
+ components: T(),
280
+ renderProfiles: T(),
281
+ loadProfiles: T(),
282
+ stateTimeline: T(),
283
+ reactiveGraph: T(),
284
+ errors: T(),
285
+ fps: T()
286
286
  };
287
287
  bump(...e) {
288
- for (let t of e) this.versions[t] = w();
288
+ for (let t of e) this.versions[t] = T();
289
289
  }
290
290
  timeline = [];
291
291
  timelineSizes = [];
292
292
  timelineEpochs = [];
293
293
  timelineBytes = 0;
294
- timelineSeq = w();
294
+ timelineSeq = T();
295
295
  timelineResetAt = this.timelineSeq;
296
296
  timelineTrimmedAt = this.timelineSeq;
297
297
  lastPull = { stateTimeline: 0 };
@@ -323,14 +323,14 @@ var we = class {
323
323
  attach(e) {
324
324
  this.detach(), this.hot = e;
325
325
  let t = [
326
- [E.runtimeReady, (e, t) => t?.send(E.subscription, this.subscription)],
327
- [E.components, (e, t) => this.ingestComponents(e, t)],
328
- [E.profiles, (e) => this.ingestProfiles(e)],
329
- [E.stateTimeline, (e) => this.ingestStateTimeline(e)],
330
- [E.fps, (e) => this.ingestFps(e)],
331
- [E.runtimeError, (e) => this.ingestRuntimeError(e)],
332
- [E.reactiveGraph, (e) => this.ingestReactiveGraph(e)],
333
- [E.reactiveSummary, (e) => this.ingestReactiveSummary(e)]
326
+ [D.runtimeReady, (e, t) => t?.send(D.subscription, this.subscription)],
327
+ [D.components, (e, t) => this.ingestComponents(e, t)],
328
+ [D.profiles, (e) => this.ingestProfiles(e)],
329
+ [D.stateTimeline, (e) => this.ingestStateTimeline(e)],
330
+ [D.fps, (e) => this.ingestFps(e)],
331
+ [D.runtimeError, (e) => this.ingestRuntimeError(e)],
332
+ [D.reactiveGraph, (e) => this.ingestReactiveGraph(e)],
333
+ [D.reactiveSummary, (e) => this.ingestReactiveSummary(e)]
334
334
  ];
335
335
  for (let [n, r] of t) e.on(n, r);
336
336
  this.detachHot = () => {
@@ -423,12 +423,12 @@ var we = class {
423
423
  let e = Date.now(), t = Infinity;
424
424
  for (let [n, r] of this.leases) r <= e ? this.leases.delete(n) : t = Math.min(t, r);
425
425
  let n = this.leases.size > 0;
426
- n !== this.active && (this.active = n, this.hot?.send(E.subscription, this.subscription)), clearTimeout(this.sweepTimer), this.sweepTimer = void 0, t !== Infinity && (this.sweepTimer = setTimeout(() => this.updateSubscription(), t - e + 1), this.sweepTimer.unref?.());
426
+ n !== this.active && (this.active = n, this.hot?.send(D.subscription, this.subscription)), clearTimeout(this.sweepTimer), this.sweepTimer = void 0, t !== Infinity && (this.sweepTimer = setTimeout(() => this.updateSubscription(), t - e + 1), this.sweepTimer.unref?.());
427
427
  }
428
428
  ingestComponents(e, t) {
429
- let n = typeof e?.epoch == "string" ? e.epoch : T, r = !Array.isArray(e?.components) && (Array.isArray(e?.added) || Array.isArray(e?.removed));
429
+ let n = typeof e?.epoch == "string" ? e.epoch : E, r = !Array.isArray(e?.components) && (Array.isArray(e?.added) || Array.isArray(e?.removed));
430
430
  if (r && !this.epochs.get(n)?.hasBase) {
431
- this.resyncRequested.has(n) || (this.resyncRequested.add(n), t?.send(E.subscription, {
431
+ this.resyncRequested.has(n) || (this.resyncRequested.add(n), t?.send(D.subscription, {
432
432
  ...this.subscription,
433
433
  resync: !0
434
434
  }));
@@ -448,7 +448,7 @@ var we = class {
448
448
  this.liveCache = void 0, this.bump("components");
449
449
  }
450
450
  ingestProfiles(e) {
451
- let t = typeof e?.epoch == "string" ? e.epoch : T, n = this.touchEpoch(t);
451
+ let t = typeof e?.epoch == "string" ? e.epoch : E, n = this.touchEpoch(t);
452
452
  n.profiles = j(e?.profiles, C.renderProfiles), n.profilesTotal = typeof e?.total == "number" && Number.isFinite(e.total) ? e.total : null, this.bump("renderProfiles");
453
453
  }
454
454
  getCaptureInfo() {
@@ -463,8 +463,8 @@ var we = class {
463
463
  "server-count": 0,
464
464
  "server-bytes": 0
465
465
  };
466
- for (let e of this.timelineDrops.values()) for (let t of O) r[t] += e[t];
467
- let i = O.filter((e) => r[e] > 0).map((e) => ({
466
+ for (let e of this.timelineDrops.values()) for (let t of k) r[t] += e[t];
467
+ let i = k.filter((e) => r[e] > 0).map((e) => ({
468
468
  reason: e,
469
469
  count: r[e]
470
470
  })), a = 0;
@@ -521,13 +521,13 @@ var we = class {
521
521
  }
522
522
  ingestStateTimeline(e) {
523
523
  let t = j(e?.changes, C.stateTimeline);
524
- if (typeof e?.epoch == "string") this.touchEpoch(e.epoch), this.countRuntimeLosses(e.epoch, e.dropped, e.valueTooLarge), e.reset === !0 && this.dropEpoch(e.epoch), this.appendTimeline(t, e.epoch), k(e.baseline) && this.timelineBaseline.set(e.epoch, {
524
+ if (typeof e?.epoch == "string") this.touchEpoch(e.epoch), this.countRuntimeLosses(e.epoch, e.dropped, e.valueTooLarge), e.reset === !0 && this.dropEpoch(e.epoch), this.appendTimeline(t, e.epoch), Se(e.baseline) && this.timelineBaseline.set(e.epoch, {
525
525
  complete: e.baseline.complete,
526
526
  pendingNodes: e.baseline.pendingNodes
527
527
  });
528
528
  else {
529
529
  let e;
530
- for (let t = this.timeline.length - 1; t >= 0 && !e; t--) this.timelineEpochs[t] === T && (e = this.timeline[t]);
530
+ for (let t = this.timeline.length - 1; t >= 0 && !e; t--) this.timelineEpochs[t] === E && (e = this.timeline[t]);
531
531
  let n = -1;
532
532
  if (e) {
533
533
  for (let r = t.length - 1; r >= 0; r--) if (t[r].timestamp === e.timestamp && t[r].id === e.id) {
@@ -535,7 +535,7 @@ var we = class {
535
535
  break;
536
536
  }
537
537
  }
538
- n === -1 && (this.dropEpoch(T), n = 0), this.appendTimeline(t.slice(n), T);
538
+ n === -1 && (this.dropEpoch(E), n = 0), this.appendTimeline(t.slice(n), E);
539
539
  }
540
540
  this.lastPull.stateTimeline = Date.now(), this.flushStateTimeline(), e?.reset === !0 && this.resolveSnapshotWaiters();
541
541
  }
@@ -573,7 +573,7 @@ var we = class {
573
573
  };
574
574
  }
575
575
  markTimelineReset() {
576
- this.timelineResetAt = this.timelineSeq = w(), this.bump("stateTimeline");
576
+ this.timelineResetAt = this.timelineSeq = T(), this.bump("stateTimeline");
577
577
  }
578
578
  dropEpoch(e) {
579
579
  if (!this.timelineEpochs.includes(e)) return;
@@ -591,7 +591,7 @@ var we = class {
591
591
  }
592
592
  this.timeline.push({
593
593
  ...n,
594
- seq: this.timelineSeq = w()
594
+ seq: this.timelineSeq = T()
595
595
  }), this.timelineSizes.push(e), this.timelineEpochs.push(t), this.timelineBytes += e;
596
596
  }
597
597
  let n = Math.max(0, this.timeline.length - C.stateTimeline), r = this.timelineBytes;
@@ -657,7 +657,7 @@ var we = class {
657
657
  n && n.kind === "summary" && (n.epoch === null || typeof e?.epoch != "string" || e.epoch === n.epoch) && this.settle(t, this.normalizeSummary(e, n.epoch));
658
658
  }
659
659
  normalizeSummary(e, t) {
660
- let n = (e) => A(e, 0, 2 ** 53 - 1, 0), r = M(e?.rows, D.maxTopK).map((e) => ({
660
+ let n = (e) => A(e, 0, 2 ** 53 - 1, 0), r = M(e?.rows, O.maxTopK).map((e) => ({
661
661
  componentId: n(e?.componentId),
662
662
  file: typeof e?.file == "string" ? e.file : "",
663
663
  nodes: {
@@ -667,7 +667,8 @@ var we = class {
667
667
  },
668
668
  changes: n(e?.changes),
669
669
  renders: n(e?.renders),
670
- renderMs: typeof e?.renderMs == "number" && e.renderMs >= 0 ? e.renderMs : 0
670
+ renderMs: typeof e?.renderMs == "number" && e.renderMs >= 0 ? e.renderMs : 0,
671
+ ...e?.kind === "module" && { kind: "module" }
671
672
  })), i = e?.window;
672
673
  return {
673
674
  epoch: typeof e?.epoch == "string" ? e.epoch : t,
@@ -694,7 +695,7 @@ var we = class {
694
695
  signalHistory: e?.capabilities?.signalHistory === !0,
695
696
  writeCause: e?.capabilities?.writeCause === !0
696
697
  },
697
- ...k(e?.baseline) && { baseline: { ...e.baseline } }
698
+ ...Se(e?.baseline) && { baseline: { ...e.baseline } }
698
699
  };
699
700
  }
700
701
  ingestFps(e) {
@@ -719,7 +720,7 @@ var we = class {
719
720
  this.fpsSamples = [], this.received.fps = 0, this.bump("fps");
720
721
  }
721
722
  clearStateTimeline() {
722
- this.timeline = [], this.timelineSizes = [], this.timelineEpochs = [], this.timelineBytes = 0, this.timelineDrops.clear(), this.timelineTooLarge.clear(), this.markTimelineReset(), this.hot?.send(E.clearStateTimeline, {});
723
+ this.timeline = [], this.timelineSizes = [], this.timelineEpochs = [], this.timelineBytes = 0, this.timelineDrops.clear(), this.timelineTooLarge.clear(), this.markTimelineReset(), this.hot?.send(D.clearStateTimeline, {});
723
724
  }
724
725
  requestReactiveGraph(e = {}) {
725
726
  let t = typeof e.componentId == "number" && Number.isInteger(e.componentId) && e.componentId >= 0 ? e.componentId : null, n = A(e.maxNodes, 1, C.reactiveNodes, C.reactiveNodes), r = A(e.maxEdges, 1, C.reactiveEdges, C.reactiveEdges), i = this.servedEpoch ?? null;
@@ -750,7 +751,7 @@ var we = class {
750
751
  staleReason: "epoch-changed"
751
752
  });
752
753
  let a = `graph|${i ?? ""}|${t ?? "*"}|${n}|${r}`;
753
- return this.keyedPull("graph", a, i, t, E.requestReactiveGraph, {
754
+ return this.keyedPull("graph", a, i, t, D.requestReactiveGraph, {
754
755
  ...t !== null && { componentId: t },
755
756
  maxNodes: n,
756
757
  maxEdges: r
@@ -769,8 +770,8 @@ var we = class {
769
770
  }));
770
771
  }
771
772
  requestReactiveSummary(e = {}) {
772
- let t = A(e.topK, 1, D.maxTopK, D.topK), n = A(e.windowMs, D.minWindowMs, D.maxWindowMs, D.windowMs), r = this.servedEpoch ?? null, i = `summary|${r ?? ""}|${t}|${n}`;
773
- return this.keyedPull("summary", i, r, null, E.requestReactiveSummary, {
773
+ let t = A(e.topK, 1, O.maxTopK, O.topK), n = A(e.windowMs, O.minWindowMs, O.maxWindowMs, O.windowMs), r = this.servedEpoch ?? null, i = `summary|${r ?? ""}|${t}|${n}`;
774
+ return this.keyedPull("summary", i, r, null, D.requestReactiveSummary, {
774
775
  topK: t,
775
776
  windowMs: n
776
777
  }, (e) => ({
@@ -815,7 +816,7 @@ var we = class {
815
816
  let u = this.hot, d = `r${++this.requestSeq}`, f = new Promise((o) => {
816
817
  let c = setTimeout(() => {
817
818
  this.pulls.delete(d), o(s("timeout"));
818
- }, be);
819
+ }, w);
819
820
  this.pulls.set(d, {
820
821
  kind: e,
821
822
  key: t,
@@ -839,7 +840,7 @@ var we = class {
839
840
  for (this.pulls.delete(e), clearTimeout(n.timer), this.pullCache.delete(n.key), this.pullCache.set(n.key, {
840
841
  value: t,
841
842
  at: Date.now()
842
- }); this.pullCache.size > Se;) this.pullCache.delete(this.pullCache.keys().next().value);
843
+ }); this.pullCache.size > xe;) this.pullCache.delete(this.pullCache.keys().next().value);
843
844
  n.kind === "graph" && this.storeGraph(t), n.resolve(t);
844
845
  }
845
846
  }
@@ -891,7 +892,7 @@ var we = class {
891
892
  }
892
893
  }
893
894
  requestStateTimeline() {
894
- return this.pull("stateTimeline", E.requestStateTimeline, this.stateTimelineResolvers, () => this.stateTimeline);
895
+ return this.pull("stateTimeline", D.requestStateTimeline, this.stateTimelineResolvers, () => this.stateTimeline);
895
896
  }
896
897
  pull(e, t, n, r) {
897
898
  if (!this.hot || Date.now() - this.lastPull[e] < 1e3) return Promise.resolve(r());
@@ -903,7 +904,7 @@ var we = class {
903
904
  }, o = setTimeout(() => {
904
905
  let t = n.indexOf(i);
905
906
  t !== -1 && n.splice(t, 1), e(r());
906
- }, be);
907
+ }, w);
907
908
  n.push(i), a.send(t, {});
908
909
  }).finally(() => {
909
910
  this.inflight[e] === o && (this.inflight[e] = void 0);
@@ -1859,34 +1860,18 @@ function $e(e, t = {}) {
1859
1860
  });
1860
1861
  }
1861
1862
  let a = /* @__PURE__ */ new Map();
1862
- for (let t of e.reactiveGraph.edges) a.set(t.from, (a.get(t.from) ?? 0) + 1);
1863
- let o = /* @__PURE__ */ new Map();
1864
- for (let t of e.reactiveGraph.edges) o.set(t.to, (o.get(t.to) ?? 0) + 1);
1865
- for (let t of e.reactiveGraph.nodes) {
1866
- if (t.type === "effect") {
1867
- let e = o.get(t.id) ?? 0;
1868
- e >= n.effectMaxDeps && r.push({
1869
- id: `effect-deps:${t.id}`,
1870
- kind: "effect-overconnected",
1871
- severity: e >= n.effectMaxDeps * 3 ? "high" : e >= n.effectMaxDeps * 2 ? "medium" : "low",
1872
- summary: `effect "${t.name}" depends on ${e} reactive values`,
1873
- file: t.componentFile,
1874
- metric: { depCount: e },
1875
- suggestedTool: "get_reactive_graph_problems"
1876
- });
1877
- }
1878
- if (t.type === "derived") {
1879
- let e = a.get(t.id) ?? 0;
1880
- e === 0 && (o.get(t.id) ?? 0) > 0 && r.push({
1881
- id: `derived-orphan:${t.id}`,
1882
- kind: "derived-orphan",
1883
- severity: "low",
1884
- summary: `derived "${t.name}" has dependencies but is unused`,
1885
- file: t.componentFile,
1886
- metric: { fanout: e },
1887
- suggestedTool: "get_reactive_graph_problems"
1888
- });
1889
- }
1863
+ for (let t of e.reactiveGraph.edges) a.set(t.to, (a.get(t.to) ?? 0) + 1);
1864
+ for (let t of e.reactiveGraph.nodes) if (t.type === "effect") {
1865
+ let e = a.get(t.id) ?? 0;
1866
+ e >= n.effectMaxDeps && r.push({
1867
+ id: `effect-deps:${t.id}`,
1868
+ kind: "effect-overconnected",
1869
+ severity: e >= n.effectMaxDeps * 3 ? "high" : e >= n.effectMaxDeps * 2 ? "medium" : "low",
1870
+ summary: `effect "${t.name}" depends on ${e} reactive values`,
1871
+ file: t.componentFile,
1872
+ metric: { depCount: e },
1873
+ suggestedTool: "get_reactive_graph_problems"
1874
+ });
1890
1875
  }
1891
1876
  return r.sort(et);
1892
1877
  }
@@ -1915,16 +1900,16 @@ function tt(e, t = {}) {
1915
1900
  name: t.name,
1916
1901
  file: t.componentFile,
1917
1902
  depCount: e
1918
- }), t.type === "derived" && c === 0 && e > 0 && o.push({
1903
+ }), t.type !== "template" && (t.type === "derived" && t.unevaluated ? o.push({
1919
1904
  id: t.id,
1920
1905
  name: t.name,
1921
1906
  file: t.componentFile
1922
- }), e === 0 && c === 0 && s.push({
1907
+ }) : e === 0 && c === 0 && !t.untrackedDeps && s.push({
1923
1908
  id: t.id,
1924
1909
  name: t.name,
1925
1910
  type: t.type,
1926
1911
  file: t.componentFile
1927
- });
1912
+ }));
1928
1913
  }
1929
1914
  return {
1930
1915
  effects: a,
@@ -1994,7 +1979,7 @@ function nt(e) {
1994
1979
  lastRenderAt: e.lastRenderAt
1995
1980
  })).sort((e, t) => t.totalRenderTimeMs - e.totalRenderTimeMs).slice(0, t))), t.registerTool("get_reactive_graph_problems", {
1996
1981
  title: "Reactive graph problems",
1997
- description: "Classified reactive graph issues: over-connected effects, orphan deriveds, isolated nodes. Returns categories instead of the full graph.",
1982
+ description: "Classified reactive graph issues: over-connected effects, orphan deriveds (declared but never evaluated so far — may still be read later, e.g. in a branch not shown yet), isolated nodes (no tracked dependency or reader; reads from the markup count as readers). Returns categories instead of the full graph.",
1998
1983
  inputSchema: { effectMaxDeps: u.number().int().min(1).optional() }
1999
1984
  }, async ({ effectMaxDeps: t }) => Z(tt(await e.getReactiveGraph(), { effectMaxDeps: t }))), t.registerTool("get_load_waterfall", {
2000
1985
  title: "SvelteKit load waterfall",
@@ -2085,7 +2070,7 @@ function nt(e) {
2085
2070
  let n = e.getReactiveSummary;
2086
2071
  t.registerTool("get_reactive_summary", {
2087
2072
  title: "Reactive overview (top components)",
2088
- description: "Busiest component instances from runtime counters over all instances, without capturing the graph. Counts are sampled state changes (at most one per state per 200 ms) and renders within the window, not rates. Only state created during component init is tracked. `rows` + `other` add up to the totals; null means unknown. Use get_reactive_scope with a componentId to look at one instance. The same request is answered from a cache for up to 1 s; `window.until` says when the answer was computed.",
2073
+ description: "Busiest component instances from runtime counters over all instances, without capturing the graph. Counts are sampled state changes (at most one per state per 200 ms) and renders within the window, not rates. Tracked: state created during component init and shared state of .svelte.js/.ts modules (rows with kind \"module\"; their componentId is a module scope id, usable with get_reactive_scope). `rows` + `other` add up to the totals; null means unknown. Use get_reactive_scope with a componentId to look at one instance. The same request is answered from a cache for up to 1 s; `window.until` says when the answer was computed.",
2089
2074
  inputSchema: {
2090
2075
  topK: u.number().int().min(1).max(200).optional(),
2091
2076
  windowMs: u.number().int().min(1e3).max(6e4).optional()
@@ -2255,7 +2240,18 @@ function pt(e) {
2255
2240
  }
2256
2241
  return t;
2257
2242
  }
2258
- function mt(r = {}) {
2243
+ function mt(e, t) {
2244
+ if (!e.includes("$.push(")) return null;
2245
+ let n = JSON.stringify(t);
2246
+ return `import '${S}';` + e.replace(/(\$\.push\([^)]+\);?)/, `if (typeof window !== 'undefined' && window.__SVELTE_DEVTOOLS__) { window.__SVELTE_DEVTOOLS__._pendingFile = ${n}; } $1`);
2247
+ }
2248
+ var ht = /\.svelte\.[cm]?[jt]s$/;
2249
+ function gt(e, t) {
2250
+ if (e.includes("svelte/internal/server")) return null;
2251
+ let n = JSON.stringify(t), r = "(typeof window !== 'undefined' && window.__SVELTE_DEVTOOLS__)";
2252
+ return `import '${S}';if (${r}) { window.__SVELTE_DEVTOOLS__._enterModule(${n}); }` + e + `\n;if (${r}) { window.__SVELTE_DEVTOOLS__._leaveModule(); }\n`;
2253
+ }
2254
+ function _t(r = {}) {
2259
2255
  let { componentTracking: i = !0 } = r, a, o, s, c = !1, l = t.randomUUID(), u = null;
2260
2256
  function d() {
2261
2257
  return u ||= new Ze({
@@ -2366,13 +2362,16 @@ function mt(r = {}) {
2366
2362
  if (e === "\0svelte-devtools:wrapped-client") return ve;
2367
2363
  }
2368
2364
  },
2369
- transformIndexHtml() {
2370
- return a.command === "serve" ? [{
2371
- tag: "script",
2372
- attrs: { type: "module" },
2373
- children: `import '${S}'`,
2374
- injectTo: "head-prepend"
2375
- }] : [];
2365
+ transformIndexHtml: {
2366
+ order: "pre",
2367
+ handler() {
2368
+ return a.command === "serve" ? [{
2369
+ tag: "script",
2370
+ attrs: { type: "module" },
2371
+ children: `import '${S}'`,
2372
+ injectTo: "head-prepend"
2373
+ }] : [];
2374
+ }
2376
2375
  },
2377
2376
  devtools: {
2378
2377
  capabilities: {
@@ -2389,11 +2388,11 @@ function mt(r = {}) {
2389
2388
  name: "vite-devtools-svelte:tracking",
2390
2389
  enforce: "post",
2391
2390
  apply: "serve",
2392
- transform(e, t) {
2393
- if (!i || !t.endsWith(".svelte") || t.includes("node_modules") || a?.command !== "serve" || !e.includes("$.push(")) return null;
2394
- let n = JSON.stringify(t);
2395
- return {
2396
- code: `import '${S}';\n` + e.replace(/(\$\.push\([^)]+\);?)/, `if (typeof window !== 'undefined' && window.__SVELTE_DEVTOOLS__) { window.__SVELTE_DEVTOOLS__._pendingFile = ${n}; }\n$1`),
2391
+ transform(e, t, n) {
2392
+ if (!i || t.includes("node_modules") || a?.command !== "serve") return null;
2393
+ let r = t.split("?")[0], o = r.endsWith(".svelte") ? mt(e, t) : ht.test(r) && !n?.ssr ? gt(e, r) : null;
2394
+ return o === null ? null : {
2395
+ code: o,
2397
2396
  map: null
2398
2397
  };
2399
2398
  }
@@ -2470,6 +2469,6 @@ export const load = async (event) => {
2470
2469
  ];
2471
2470
  }
2472
2471
  //#endregion
2473
- export { mt as svelteDevtools };
2472
+ export { _t as svelteDevtools };
2474
2473
 
2475
2474
  //# sourceMappingURL=index.mjs.map