vite-devtools-svelte 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -6
- package/dist/client/assets/client-BF-DBa_K.js +4 -0
- package/dist/client/assets/index-CnFg6eG8.js +6 -0
- package/dist/client/index.html +1 -1
- package/dist/index.mjs +725 -136
- package/dist/index.mjs.map +1 -1
- package/dist/mcp/issues.d.ts +52 -0
- package/dist/mcp/server.d.ts +18 -0
- package/dist/mcp/sessions.d.ts +114 -0
- package/dist/template-injector.d.ts +42 -0
- package/package.json +10 -7
- package/skills/vite-devtools-svelte-perf-audit/SKILL.md +31 -0
- package/skills/vite-devtools-svelte-perf-fix/SKILL.md +31 -0
- package/dist/client/assets/client-HxayJ2w5.js +0 -4
- package/dist/client/assets/index-BtpbdjQx.js +0 -6
package/dist/index.mjs
CHANGED
|
@@ -6,24 +6,27 @@ import { fileURLToPath as i } from "node:url";
|
|
|
6
6
|
import { execFile as a } from "node:child_process";
|
|
7
7
|
import o from "fs";
|
|
8
8
|
import s from "path";
|
|
9
|
+
import { McpServer as c } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StreamableHTTPServerTransport as l } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
11
|
+
import { z as u } from "zod";
|
|
9
12
|
//#region src/analyzers/routes.ts
|
|
10
|
-
function
|
|
13
|
+
function d(e) {
|
|
11
14
|
if (!o.existsSync(e)) return [];
|
|
12
15
|
let t = [];
|
|
13
|
-
return
|
|
16
|
+
return f(e, e, t), t.sort((e, t) => e.path.localeCompare(t.path));
|
|
14
17
|
}
|
|
15
|
-
function
|
|
18
|
+
function f(e, t, n) {
|
|
16
19
|
let r = o.readdirSync(e, { withFileTypes: !0 }), i = [], a = !1;
|
|
17
20
|
for (let o of r) {
|
|
18
21
|
if (o.isDirectory()) {
|
|
19
|
-
|
|
22
|
+
f(s.join(e, o.name), t, n);
|
|
20
23
|
continue;
|
|
21
24
|
}
|
|
22
|
-
let r =
|
|
25
|
+
let r = p(o.name, s.join(e, o.name));
|
|
23
26
|
r && (i.push(r), a = !0);
|
|
24
27
|
}
|
|
25
28
|
if (a) {
|
|
26
|
-
let r = s.relative(t, e), a =
|
|
29
|
+
let r = s.relative(t, e), a = m(r), o = h(r);
|
|
27
30
|
n.push({
|
|
28
31
|
id: r || "/",
|
|
29
32
|
path: a,
|
|
@@ -41,7 +44,7 @@ function l(e, t, n) {
|
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
function
|
|
47
|
+
function p(e, t) {
|
|
45
48
|
let n = {
|
|
46
49
|
"+page.svelte": "page",
|
|
47
50
|
"+layout.svelte": "layout",
|
|
@@ -62,7 +65,7 @@ function u(e, t) {
|
|
|
62
65
|
path: t
|
|
63
66
|
} : null;
|
|
64
67
|
}
|
|
65
|
-
function
|
|
68
|
+
function m(e) {
|
|
66
69
|
return e ? "/" + e.split(s.sep).map((e) => {
|
|
67
70
|
if (e.startsWith("(") && e.endsWith(")")) return null;
|
|
68
71
|
if (e.startsWith("[") && e.endsWith("]")) {
|
|
@@ -72,7 +75,7 @@ function d(e) {
|
|
|
72
75
|
return e;
|
|
73
76
|
}).filter(Boolean).join("/") : "/";
|
|
74
77
|
}
|
|
75
|
-
function
|
|
78
|
+
function h(e) {
|
|
76
79
|
if (!e) return [];
|
|
77
80
|
let t = [], n = e.split(s.sep);
|
|
78
81
|
for (let e of n) {
|
|
@@ -102,7 +105,7 @@ function f(e) {
|
|
|
102
105
|
}
|
|
103
106
|
//#endregion
|
|
104
107
|
//#region src/analyzers/assets.ts
|
|
105
|
-
var
|
|
108
|
+
var g = {
|
|
106
109
|
".png": "image/png",
|
|
107
110
|
".jpg": "image/jpeg",
|
|
108
111
|
".jpeg": "image/jpeg",
|
|
@@ -129,17 +132,17 @@ var p = {
|
|
|
129
132
|
".js": "text/javascript",
|
|
130
133
|
".html": "text/html"
|
|
131
134
|
};
|
|
132
|
-
function
|
|
135
|
+
function _(e) {
|
|
133
136
|
if (!o.existsSync(e)) return [];
|
|
134
137
|
let t = [];
|
|
135
|
-
return
|
|
138
|
+
return v(e, e, t), t.sort((e, t) => e.relativePath.localeCompare(t.relativePath));
|
|
136
139
|
}
|
|
137
|
-
function
|
|
140
|
+
function v(e, t, n) {
|
|
138
141
|
let r = o.readdirSync(e, { withFileTypes: !0 });
|
|
139
142
|
for (let i of r) {
|
|
140
143
|
let r = s.join(e, i.name);
|
|
141
144
|
if (i.isDirectory()) {
|
|
142
|
-
|
|
145
|
+
v(r, t, n);
|
|
143
146
|
continue;
|
|
144
147
|
}
|
|
145
148
|
if (i.name.startsWith(".")) continue;
|
|
@@ -149,59 +152,59 @@ function h(e, t, n) {
|
|
|
149
152
|
path: r,
|
|
150
153
|
relativePath: s.relative(t, r),
|
|
151
154
|
size: a.size,
|
|
152
|
-
type:
|
|
155
|
+
type: g[c] || "application/octet-stream",
|
|
153
156
|
mtime: a.mtimeMs
|
|
154
157
|
});
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
//#endregion
|
|
158
161
|
//#region src/analyzers/project.ts
|
|
159
|
-
var
|
|
160
|
-
function
|
|
162
|
+
var y = 5e3, b = null, x = null, S = 0;
|
|
163
|
+
function C(e) {
|
|
161
164
|
let t = Date.now();
|
|
162
|
-
if (
|
|
163
|
-
let n =
|
|
164
|
-
return
|
|
165
|
+
if (b && x === e && t - S < y) return b;
|
|
166
|
+
let n = w(e);
|
|
167
|
+
return b = n, x = e, S = t, n;
|
|
165
168
|
}
|
|
166
|
-
function
|
|
169
|
+
function w(e) {
|
|
167
170
|
let t = s.join(e, "package.json"), n = o.existsSync(t) ? JSON.parse(o.readFileSync(t, "utf-8")) : {}, r = n.dependencies || {}, i = n.devDependencies || {};
|
|
168
171
|
return {
|
|
169
172
|
name: n.name || s.basename(e),
|
|
170
173
|
version: n.version || "0.0.0",
|
|
171
|
-
svelteVersion:
|
|
172
|
-
sveltekitVersion:
|
|
173
|
-
viteVersion:
|
|
174
|
+
svelteVersion: T(e, "svelte") || r.svelte || i.svelte || "unknown",
|
|
175
|
+
sveltekitVersion: T(e, "@sveltejs/kit") || r["@sveltejs/kit"] || i["@sveltejs/kit"] || "unknown",
|
|
176
|
+
viteVersion: T(e, "vite") || r.vite || i.vite || "unknown",
|
|
174
177
|
dependencies: r,
|
|
175
178
|
devDependencies: i,
|
|
176
|
-
routesDir:
|
|
177
|
-
staticDir:
|
|
179
|
+
routesDir: E(e),
|
|
180
|
+
staticDir: D(e)
|
|
178
181
|
};
|
|
179
182
|
}
|
|
180
|
-
function
|
|
183
|
+
function T(e, t) {
|
|
181
184
|
try {
|
|
182
185
|
let n = s.join(e, "node_modules", t, "package.json");
|
|
183
186
|
if (o.existsSync(n)) return JSON.parse(o.readFileSync(n, "utf-8")).version;
|
|
184
187
|
} catch {}
|
|
185
188
|
return null;
|
|
186
189
|
}
|
|
187
|
-
function
|
|
190
|
+
function E(e) {
|
|
188
191
|
let t = [s.join(e, "src", "routes"), s.join(e, "src", "pages")];
|
|
189
192
|
for (let e of t) if (o.existsSync(e)) return e;
|
|
190
193
|
return s.join(e, "src", "routes");
|
|
191
194
|
}
|
|
192
|
-
function
|
|
195
|
+
function D(e) {
|
|
193
196
|
let t = [s.join(e, "static"), s.join(e, "public")];
|
|
194
197
|
for (let e of t) if (o.existsSync(e)) return e;
|
|
195
198
|
return s.join(e, "static");
|
|
196
199
|
}
|
|
197
200
|
//#endregion
|
|
198
201
|
//#region src/analyzers/components.ts
|
|
199
|
-
function
|
|
202
|
+
function O(e) {
|
|
200
203
|
let t = s.join(e, "src");
|
|
201
204
|
if (!o.existsSync(t)) return [];
|
|
202
205
|
let n = [];
|
|
203
|
-
return
|
|
204
|
-
let n =
|
|
206
|
+
return k(t, n), n.map((t) => {
|
|
207
|
+
let n = A(o.readFileSync(t, "utf-8"), t), r = N(t);
|
|
205
208
|
return {
|
|
206
209
|
file: s.relative(e, t),
|
|
207
210
|
name: r,
|
|
@@ -209,27 +212,27 @@ function T(e) {
|
|
|
209
212
|
};
|
|
210
213
|
});
|
|
211
214
|
}
|
|
212
|
-
function
|
|
215
|
+
function k(e, t) {
|
|
213
216
|
let n = o.readdirSync(e, { withFileTypes: !0 });
|
|
214
217
|
for (let r of n) {
|
|
215
218
|
let n = s.join(e, r.name);
|
|
216
219
|
if (r.isDirectory()) {
|
|
217
220
|
if (r.name === "node_modules" || r.name === ".svelte-kit") continue;
|
|
218
|
-
|
|
221
|
+
k(n, t);
|
|
219
222
|
} else r.name.endsWith(".svelte") && t.push(n);
|
|
220
223
|
}
|
|
221
224
|
}
|
|
222
|
-
function
|
|
225
|
+
function A(e, t) {
|
|
223
226
|
let n = [], r = s.dirname(t), i = /import\s+[\w{}\s,*]+\s+from\s+['"]([^'"]+\.svelte)['"]/g, a;
|
|
224
227
|
for (; (a = i.exec(e)) !== null;) {
|
|
225
|
-
let e = a[1], i =
|
|
228
|
+
let e = a[1], i = j(e, r, t);
|
|
226
229
|
i && n.push(i);
|
|
227
230
|
}
|
|
228
231
|
return n;
|
|
229
232
|
}
|
|
230
|
-
function
|
|
233
|
+
function j(e, t, n) {
|
|
231
234
|
if (e.startsWith("$lib/")) {
|
|
232
|
-
let t =
|
|
235
|
+
let t = M(n);
|
|
233
236
|
if (t) {
|
|
234
237
|
let n = s.join(t, "src", "lib", e.slice(5));
|
|
235
238
|
if (o.existsSync(n)) return n;
|
|
@@ -242,7 +245,7 @@ function O(e, t, n) {
|
|
|
242
245
|
}
|
|
243
246
|
return null;
|
|
244
247
|
}
|
|
245
|
-
function
|
|
248
|
+
function M(e) {
|
|
246
249
|
let t = s.dirname(e);
|
|
247
250
|
for (; t !== s.dirname(t);) {
|
|
248
251
|
if (o.existsSync(s.join(t, "package.json"))) return t;
|
|
@@ -250,13 +253,547 @@ function k(e) {
|
|
|
250
253
|
}
|
|
251
254
|
return null;
|
|
252
255
|
}
|
|
253
|
-
function
|
|
256
|
+
function N(e) {
|
|
254
257
|
return s.basename(e, ".svelte");
|
|
255
258
|
}
|
|
256
259
|
//#endregion
|
|
257
260
|
//#region src/runtime.ts
|
|
258
|
-
var j = "virtual:svelte-devtools-runtime", M = "\0" + j, N = "\0svelte-devtools:wrapped-client", P = "\nimport * as __svelte_original from 'svelte/internal/client';\nexport * from 'svelte/internal/client';\n\nfunction __dt() {\n return typeof window !== 'undefined' ? window.__SVELTE_DEVTOOLS__ : null;\n}\n\n// Component ID stack (parallel to runtime._stack but local to wrapper)\nconst __idStack = [];\nfunction __currentId() {\n return __idStack.length > 0 ? __idStack[__idStack.length - 1] : null;\n}\n\n// Tracks the most recently created signal for type determination in tag()\nconst __pendingSignal = { ref: null, type: null };\n\n// --- Component Lifecycle ---\n//\n// Observer must not break the observed: every devtools side-effect below is\n// wrapped in try/catch so a bug or an out-of-shape runtime cannot tear down\n// the user's app. The original Svelte function is always called, and its\n// return value always returned, regardless of devtools failures.\n\nexport function push() {\n const result = __svelte_original.push.apply(null, arguments);\n try {\n const dt = __dt();\n if (dt) {\n const file = dt._pendingFile || 'Unknown';\n dt._pendingFile = null;\n const id = dt.register(file);\n __idStack.push(id);\n dt.startInit(id);\n try {\n __svelte_original.user_effect(() => {\n return () => { try { dt.unmount(id); } catch {} };\n });\n } catch {}\n }\n } catch {}\n return result;\n}\n\nexport function pop() {\n // Capture devtools errors but ALWAYS forward to the original pop so the\n // Svelte component stack stays balanced.\n try {\n const dt = __dt();\n if (dt && __idStack.length > 0) {\n const id = __idStack.pop();\n try { dt.endInit(id); } catch {}\n try { dt.registered(id); } catch {}\n }\n } catch {}\n return __svelte_original.pop.apply(null, arguments);\n}\n\n// --- Signal Creation (type markers) ---\n\nexport function state() {\n const signal = __svelte_original.state.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'state';\n } catch {}\n return signal;\n}\n\nexport function derived() {\n const signal = __svelte_original.derived.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'derived';\n } catch {}\n return signal;\n}\n\nexport function proxy() {\n const p = __svelte_original.proxy.apply(null, arguments);\n try {\n __pendingSignal.ref = p;\n __pendingSignal.type = 'proxy';\n } catch {}\n return p;\n}\n\n// --- Signal Tagging (Svelte dev mode) ---\n\nexport function tag(signal, name) {\n const result = __svelte_original.tag.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n const type = (__pendingSignal.ref === signal) ? __pendingSignal.type : 'state';\n if (type === 'derived') {\n dt.trackDerived(signal, name, cid);\n } else {\n dt.trackState(signal, name, cid);\n }\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\nexport function tag_proxy(proxy, name) {\n const result = __svelte_original.tag_proxy.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt.trackProxy(proxy, name, cid);\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\n// --- Effect Tracking ---\n\nexport function user_effect() {\n const result = __svelte_original.user_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n // Track the effect OBJECT (not the callback). The Svelte runtime\n // populates result.deps with the signals this effect depends on,\n // which is what getReactiveGraph() reads to build edges.\n dt.trackEffect(result, 'effect_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n\nexport function user_pre_effect() {\n const result = __svelte_original.user_pre_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n dt.trackEffect(result, 'effect_pre_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n", F = "\nif (typeof window !== 'undefined' && !window.__SVELTE_DEVTOOLS__) {\n const __SVELTE_DT = {\n _nextId: 0,\n _instances: new Map(),\n _stack: [],\n _pendingFile: null,\n _effectCounter: 0,\n _debounceTimer: null,\n _listeners: new Set(),\n\n // Phase 2: Profiling data\n _profiles: new Map(),\n _initStartTimes: new Map(),\n _profileDebounceTimer: null,\n\n // Phase 2: Reactive graph data\n _reactiveNodes: new Map(),\n _reactiveProxies: new Map(),\n\n // Phase 3: State timeline\n _stateTimeline: [],\n _stateSnapshots: new Map(),\n _timelineDebounceTimer: null,\n\n register(file) {\n const id = this._nextId++;\n const parentId = this._stack.length > 0 ? this._stack[this._stack.length - 1] : null;\n const name = file.split('/').pop()?.replace('.svelte', '') || 'Unknown';\n this._instances.set(id, { id, file, name, parentId, mounted: true, children: [] });\n if (parentId !== null) {\n const parent = this._instances.get(parentId);\n if (parent) parent.children.push(id);\n }\n this._stack.push(id);\n this._scheduleUpdate();\n return id;\n },\n\n registered(id) {\n const idx = this._stack.indexOf(id);\n if (idx !== -1) this._stack.splice(idx, 1);\n },\n\n mount(id) {\n const instance = this._instances.get(id);\n if (instance && !instance.mounted) {\n instance.mounted = true;\n this._scheduleUpdate();\n }\n },\n\n unmount(id) {\n const instance = this._instances.get(id);\n if (instance) {\n if (instance.parentId !== null) {\n const parent = this._instances.get(instance.parentId);\n if (parent) {\n parent.children = parent.children.filter(cid => cid !== id);\n }\n }\n this._removeChildren(id);\n this._cleanupReactiveNodes(id);\n this._instances.delete(id);\n }\n this._scheduleUpdate();\n },\n\n _cleanupReactiveNodes(componentId) {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.componentId === componentId) {\n this._reactiveNodes.delete(nodeId);\n }\n }\n },\n\n _removeChildren(parentId) {\n const parent = this._instances.get(parentId);\n if (!parent) return;\n for (const childId of [...parent.children]) {\n this._removeChildren(childId);\n this._cleanupReactiveNodes(childId);\n this._instances.delete(childId);\n }\n },\n\n _scheduleUpdate() {\n if (this._debounceTimer) clearTimeout(this._debounceTimer);\n this._debounceTimer = setTimeout(() => {\n this._sendUpdate();\n }, 100);\n },\n\n _sendUpdate() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:components', { components });\n }\n },\n\n getTree() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n return components;\n },\n\n // --- Phase 2: Render Profiling ---\n\n startInit(id) {\n this._initStartTimes.set(id, performance.now());\n },\n\n endInit(id) {\n const start = this._initStartTimes.get(id);\n if (start === undefined) return;\n const initTime = performance.now() - start;\n this._initStartTimes.delete(id);\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime,\n renderCount: 0,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n this._scheduleProfileUpdate();\n },\n\n recordRender(id) {\n const profile = this._profiles.get(id);\n if (!profile) {\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime: 0,\n renderCount: 1,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n } else {\n profile.renderCount++;\n profile.lastRenderAt = Date.now();\n }\n this._scheduleProfileUpdate();\n },\n\n recordRenderTime(id, duration) {\n const profile = this._profiles.get(id);\n if (profile) {\n profile.totalRenderTime += duration;\n profile.lastRenderTime = duration;\n }\n },\n\n _scheduleProfileUpdate() {\n if (this._profileDebounceTimer) clearTimeout(this._profileDebounceTimer);\n this._profileDebounceTimer = setTimeout(() => {\n this._sendProfileUpdate();\n }, 500);\n },\n\n _sendProfileUpdate() {\n const profiles = Array.from(this._profiles.values());\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:profiles', { profiles });\n }\n },\n\n getProfiles() {\n return Array.from(this._profiles.values());\n },\n\n resetProfiles() {\n this._profiles.clear();\n this._scheduleProfileUpdate();\n },\n\n // --- Phase 2: Reactive Graph Tracking ---\n\n trackState(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackProxy(proxy, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveProxies.set(nodeId, new WeakRef(proxy));\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef({ v: '(proxy)', _isProxy: true }),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackDerived(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'derived', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackEffect(effect, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(effect || { v: undefined, _isEffect: true }),\n meta: { id: nodeId, type: 'effect', name, componentId, componentFile: instance ? instance.file : '' }\n });\n return effect;\n },\n\n getReactiveGraph() {\n const nodes = [];\n const edges = [];\n const signalToId = new Map();\n\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal || !this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n signalToId.set(signal, nodeId);\n const node = { ...entry.meta };\n if (signal._isProxy) {\n const proxyRef = this._reactiveProxies.get(nodeId);\n const proxy = proxyRef?.deref();\n if (proxy) {\n try {\n node.value = Array.isArray(proxy) ? '[' + proxy.length + ']' : '{' + Object.keys(proxy).length + '}';\n } catch { node.value = '(proxy)'; }\n }\n } else if (node.type !== 'effect' && signal.v !== undefined && typeof signal.v !== 'symbol') {\n try {\n const v = signal.v;\n if (typeof v === 'number' || typeof v === 'string' || typeof v === 'boolean' || v === null) {\n node.value = v;\n } else {\n node.value = '(object)';\n }\n } catch { /* ignore */ }\n }\n nodes.push(node);\n }\n\n // Map proxy internal signals to their proxy node ID\n for (const [nodeId, ref] of this._reactiveProxies) {\n const proxy = ref.deref();\n if (!proxy) { this._reactiveProxies.delete(nodeId); continue; }\n const meta = this._reactiveNodes.get(nodeId)?.meta;\n if (!meta || !this._instances.has(meta.componentId)) { this._reactiveProxies.delete(nodeId); continue; }\n }\n\n // Build edges from deps\n const edgeSet = new Set();\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal) continue;\n if (!signal.deps) continue;\n\n const visited = new Set();\n const queue = [...signal.deps];\n while (queue.length > 0) {\n const dep = queue.shift();\n if (!dep || visited.has(dep)) continue;\n visited.add(dep);\n const depId = signalToId.get(dep);\n if (depId) {\n const key = depId + '>' + nodeId;\n if (depId !== nodeId && !edgeSet.has(key)) {\n edgeSet.add(key);\n edges.push({ from: depId, to: nodeId });\n }\n } else if (dep.deps) {\n for (const d of dep.deps) queue.push(d);\n }\n }\n }\n\n return { nodes, edges };\n },\n\n sendReactiveGraph() {\n const graph = this.getReactiveGraph();\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-graph', graph);\n }\n },\n\n // --- Phase 3: State Timeline ---\n\n _pollStateValues() {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.type !== 'state') continue;\n if (!this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n const signal = entry.signal.deref();\n if (!signal || signal.v === undefined) continue;\n try {\n const currentVal = signal.v;\n const prev = this._stateSnapshots.get(nodeId);\n if (prev !== undefined && currentVal === prev) continue;\n const isPrimitive = currentVal === null || typeof currentVal !== 'object';\n let newSnapshot;\n if (isPrimitive) {\n if (prev !== undefined && prev === currentVal) continue;\n newSnapshot = currentVal;\n } else {\n const currStr = JSON.stringify(currentVal);\n const prevStr = this._stateSnapshotStrs?.get(nodeId);\n if (prevStr === currStr) continue;\n newSnapshot = JSON.parse(currStr);\n if (!this._stateSnapshotStrs) this._stateSnapshotStrs = new Map();\n this._stateSnapshotStrs.set(nodeId, currStr);\n }\n if (this._stateTimeline.length >= 500) this._stateTimeline.splice(0, this._stateTimeline.length - 499);\n this._stateTimeline.push({\n id: nodeId,\n name: entry.meta.name,\n componentFile: entry.meta.componentFile,\n oldValue: prev !== undefined ? prev : null,\n newValue: newSnapshot,\n timestamp: Date.now(),\n });\n this._stateSnapshots.set(nodeId, newSnapshot);\n this._scheduleTimelineUpdate();\n } catch { /* ignore non-serializable */ }\n }\n },\n\n _scheduleTimelineUpdate() {\n if (this._timelineDebounceTimer) clearTimeout(this._timelineDebounceTimer);\n this._timelineDebounceTimer = setTimeout(() => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: this._stateTimeline });\n }\n }, 300);\n },\n\n getStateTimeline() {\n return this._stateTimeline;\n },\n\n clearStateTimeline() {\n this._stateTimeline = [];\n this._scheduleTimelineUpdate();\n },\n\n // --- FPS Monitoring ---\n // Uses requestAnimationFrame to measure frame rate with minimal overhead.\n // Only stores timestamps - no allocations per frame beyond a single array push.\n _fpsFrameTimes: [],\n\n _fpsLoop() {\n this._fpsFrameTimes.push(performance.now());\n requestAnimationFrame(() => this._fpsLoop());\n },\n\n _sampleFps() {\n const now = performance.now();\n // Remove frame timestamps older than 1 second\n const cutoff = now - 1000;\n let i = 0;\n while (i < this._fpsFrameTimes.length && this._fpsFrameTimes[i] < cutoff) i++;\n if (i > 0) this._fpsFrameTimes.splice(0, i);\n const fps = this._fpsFrameTimes.length;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:fps', { timestamp: Date.now(), fps });\n }\n }\n };\n\n window.__SVELTE_DEVTOOLS__ = __SVELTE_DT;\n\n // Poll state values for timeline\n setInterval(() => { __SVELTE_DT._pollStateValues(); }, 200);\n\n // FPS monitoring\n requestAnimationFrame(() => __SVELTE_DT._fpsLoop());\n setInterval(() => __SVELTE_DT._sampleFps(), 500);\n\n // Phase 3: Capture runtime errors\n window.addEventListener('error', (event) => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno,\n stack: event.error?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n window.addEventListener('unhandledrejection', (event) => {\n if (import.meta.hot) {\n const reason = event.reason;\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: reason?.message || String(reason),\n stack: reason?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n\n // Listen for reactive graph requests from server\n if (import.meta.hot) {\n import.meta.hot.on('svelte-devtools:request-reactive-graph', () => {\n __SVELTE_DT.sendReactiveGraph();\n });\n import.meta.hot.on('svelte-devtools:request-state-timeline', () => {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: __SVELTE_DT._stateTimeline });\n });\n import.meta.hot.on('svelte-devtools:clear-state-timeline', () => {\n __SVELTE_DT.clearStateTimeline();\n });\n }\n}\n", I = e.dirname(i(import.meta.url));
|
|
259
|
-
function
|
|
261
|
+
var P = "virtual:svelte-devtools-runtime", ee = "\0virtual:svelte-devtools-runtime", F = "\0svelte-devtools:wrapped-client", I = "\nimport * as __svelte_original from 'svelte/internal/client';\nexport * from 'svelte/internal/client';\n\nfunction __dt() {\n return typeof window !== 'undefined' ? window.__SVELTE_DEVTOOLS__ : null;\n}\n\n// Component ID stack (parallel to runtime._stack but local to wrapper)\nconst __idStack = [];\nfunction __currentId() {\n return __idStack.length > 0 ? __idStack[__idStack.length - 1] : null;\n}\n\n// Tracks the most recently created signal for type determination in tag()\nconst __pendingSignal = { ref: null, type: null };\n\n// --- Component Lifecycle ---\n//\n// Observer must not break the observed: every devtools side-effect below is\n// wrapped in try/catch so a bug or an out-of-shape runtime cannot tear down\n// the user's app. The original Svelte function is always called, and its\n// return value always returned, regardless of devtools failures.\n\nexport function push() {\n const result = __svelte_original.push.apply(null, arguments);\n try {\n const dt = __dt();\n if (dt) {\n const file = dt._pendingFile || 'Unknown';\n dt._pendingFile = null;\n const id = dt.register(file);\n __idStack.push(id);\n dt.startInit(id);\n try {\n __svelte_original.user_effect(() => {\n return () => { try { dt.unmount(id); } catch {} };\n });\n } catch {}\n }\n } catch {}\n return result;\n}\n\nexport function pop() {\n // Capture devtools errors but ALWAYS forward to the original pop so the\n // Svelte component stack stays balanced.\n try {\n const dt = __dt();\n if (dt && __idStack.length > 0) {\n const id = __idStack.pop();\n try { dt.endInit(id); } catch {}\n try { dt.registered(id); } catch {}\n }\n } catch {}\n return __svelte_original.pop.apply(null, arguments);\n}\n\n// --- Signal Creation (type markers) ---\n\nexport function state() {\n const signal = __svelte_original.state.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'state';\n } catch {}\n return signal;\n}\n\nexport function derived() {\n const signal = __svelte_original.derived.apply(null, arguments);\n try {\n __pendingSignal.ref = signal;\n __pendingSignal.type = 'derived';\n } catch {}\n return signal;\n}\n\nexport function proxy() {\n const p = __svelte_original.proxy.apply(null, arguments);\n try {\n __pendingSignal.ref = p;\n __pendingSignal.type = 'proxy';\n } catch {}\n return p;\n}\n\n// --- Signal Tagging (Svelte dev mode) ---\n\nexport function tag(signal, name) {\n const result = __svelte_original.tag.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n const type = (__pendingSignal.ref === signal) ? __pendingSignal.type : 'state';\n if (type === 'derived') {\n dt.trackDerived(signal, name, cid);\n } else {\n dt.trackState(signal, name, cid);\n }\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\nexport function tag_proxy(proxy, name) {\n const result = __svelte_original.tag_proxy.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt.trackProxy(proxy, name, cid);\n }\n __pendingSignal.ref = null;\n __pendingSignal.type = null;\n } catch {}\n return result;\n}\n\n// --- Effect Tracking ---\n\nexport function user_effect() {\n const result = __svelte_original.user_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n // Track the effect OBJECT (not the callback). The Svelte runtime\n // populates result.deps with the signals this effect depends on,\n // which is what getReactiveGraph() reads to build edges.\n dt.trackEffect(result, 'effect_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n\nexport function user_pre_effect() {\n const result = __svelte_original.user_pre_effect.apply(null, arguments);\n try {\n const dt = __dt();\n const cid = __currentId();\n if (dt && cid !== null) {\n dt._effectCounter = (dt._effectCounter || 0) + 1;\n dt.trackEffect(result, 'effect_pre_' + dt._effectCounter, cid);\n }\n } catch {}\n return result;\n}\n", L = "\nif (typeof window !== 'undefined' && !window.__SVELTE_DEVTOOLS__) {\n const __SVELTE_DT = {\n _nextId: 0,\n _instances: new Map(),\n _stack: [],\n _pendingFile: null,\n _effectCounter: 0,\n _debounceTimer: null,\n _listeners: new Set(),\n\n // Phase 2: Profiling data\n _profiles: new Map(),\n _initStartTimes: new Map(),\n _profileDebounceTimer: null,\n\n // Phase 2: Reactive graph data\n _reactiveNodes: new Map(),\n _reactiveProxies: new Map(),\n\n // Phase 3: State timeline\n _stateTimeline: [],\n _stateSnapshots: new Map(),\n _timelineDebounceTimer: null,\n\n register(file) {\n const id = this._nextId++;\n const parentId = this._stack.length > 0 ? this._stack[this._stack.length - 1] : null;\n const name = file.split('/').pop()?.replace('.svelte', '') || 'Unknown';\n this._instances.set(id, { id, file, name, parentId, mounted: true, children: [] });\n if (parentId !== null) {\n const parent = this._instances.get(parentId);\n if (parent) parent.children.push(id);\n }\n this._stack.push(id);\n this._scheduleUpdate();\n return id;\n },\n\n registered(id) {\n const idx = this._stack.indexOf(id);\n if (idx !== -1) this._stack.splice(idx, 1);\n },\n\n mount(id) {\n const instance = this._instances.get(id);\n if (instance && !instance.mounted) {\n instance.mounted = true;\n this._scheduleUpdate();\n }\n },\n\n unmount(id) {\n const instance = this._instances.get(id);\n if (instance) {\n if (instance.parentId !== null) {\n const parent = this._instances.get(instance.parentId);\n if (parent) {\n parent.children = parent.children.filter(cid => cid !== id);\n }\n }\n this._removeChildren(id);\n this._cleanupReactiveNodes(id);\n this._instances.delete(id);\n }\n this._scheduleUpdate();\n },\n\n _cleanupReactiveNodes(componentId) {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.componentId === componentId) {\n this._reactiveNodes.delete(nodeId);\n }\n }\n },\n\n _removeChildren(parentId) {\n const parent = this._instances.get(parentId);\n if (!parent) return;\n for (const childId of [...parent.children]) {\n this._removeChildren(childId);\n this._cleanupReactiveNodes(childId);\n this._instances.delete(childId);\n }\n },\n\n _scheduleUpdate() {\n if (this._debounceTimer) clearTimeout(this._debounceTimer);\n this._debounceTimer = setTimeout(() => {\n this._sendUpdate();\n }, 100);\n },\n\n _sendUpdate() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:components', { components });\n }\n },\n\n getTree() {\n const components = [];\n for (const [, instance] of this._instances) {\n if (instance.mounted) {\n components.push({\n id: instance.id,\n file: instance.file,\n name: instance.name,\n parentId: instance.parentId,\n mounted: instance.mounted,\n });\n }\n }\n return components;\n },\n\n // --- Phase 2: Render Profiling ---\n\n startInit(id) {\n this._initStartTimes.set(id, performance.now());\n },\n\n endInit(id) {\n const start = this._initStartTimes.get(id);\n if (start === undefined) return;\n const initTime = performance.now() - start;\n this._initStartTimes.delete(id);\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime,\n renderCount: 0,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n this._scheduleProfileUpdate();\n },\n\n recordRender(id) {\n const profile = this._profiles.get(id);\n if (!profile) {\n const instance = this._instances.get(id);\n if (!instance) return;\n this._profiles.set(id, {\n componentId: id,\n file: instance.file,\n name: instance.name,\n initTime: 0,\n renderCount: 1,\n totalRenderTime: 0,\n lastRenderTime: 0,\n lastRenderAt: Date.now(),\n });\n } else {\n profile.renderCount++;\n profile.lastRenderAt = Date.now();\n }\n this._scheduleProfileUpdate();\n },\n\n recordRenderTime(id, duration) {\n const profile = this._profiles.get(id);\n if (profile) {\n profile.totalRenderTime += duration;\n profile.lastRenderTime = duration;\n }\n },\n\n _scheduleProfileUpdate() {\n if (this._profileDebounceTimer) clearTimeout(this._profileDebounceTimer);\n this._profileDebounceTimer = setTimeout(() => {\n this._sendProfileUpdate();\n }, 500);\n },\n\n _sendProfileUpdate() {\n const profiles = Array.from(this._profiles.values());\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:profiles', { profiles });\n }\n },\n\n getProfiles() {\n return Array.from(this._profiles.values());\n },\n\n resetProfiles() {\n this._profiles.clear();\n this._scheduleProfileUpdate();\n },\n\n // --- Phase 2: Reactive Graph Tracking ---\n\n trackState(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackProxy(proxy, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveProxies.set(nodeId, new WeakRef(proxy));\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef({ v: '(proxy)', _isProxy: true }),\n meta: { id: nodeId, type: 'state', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackDerived(signal, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(signal),\n meta: { id: nodeId, type: 'derived', name, componentId, componentFile: instance ? instance.file : '' }\n });\n },\n\n trackEffect(effect, name, componentId) {\n const instance = this._instances.get(componentId);\n const nodeId = componentId + ':' + name;\n this._reactiveNodes.set(nodeId, {\n signal: new WeakRef(effect || { v: undefined, _isEffect: true }),\n meta: { id: nodeId, type: 'effect', name, componentId, componentFile: instance ? instance.file : '' }\n });\n return effect;\n },\n\n getReactiveGraph() {\n const nodes = [];\n const edges = [];\n const signalToId = new Map();\n\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal || !this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n signalToId.set(signal, nodeId);\n const node = { ...entry.meta };\n if (signal._isProxy) {\n const proxyRef = this._reactiveProxies.get(nodeId);\n const proxy = proxyRef?.deref();\n if (proxy) {\n try {\n node.value = Array.isArray(proxy) ? '[' + proxy.length + ']' : '{' + Object.keys(proxy).length + '}';\n } catch { node.value = '(proxy)'; }\n }\n } else if (node.type !== 'effect' && signal.v !== undefined && typeof signal.v !== 'symbol') {\n try {\n const v = signal.v;\n if (typeof v === 'number' || typeof v === 'string' || typeof v === 'boolean' || v === null) {\n node.value = v;\n } else {\n node.value = '(object)';\n }\n } catch { /* ignore */ }\n }\n nodes.push(node);\n }\n\n // Map proxy internal signals to their proxy node ID\n for (const [nodeId, ref] of this._reactiveProxies) {\n const proxy = ref.deref();\n if (!proxy) { this._reactiveProxies.delete(nodeId); continue; }\n const meta = this._reactiveNodes.get(nodeId)?.meta;\n if (!meta || !this._instances.has(meta.componentId)) { this._reactiveProxies.delete(nodeId); continue; }\n }\n\n // Build edges from deps\n const edgeSet = new Set();\n for (const [nodeId, entry] of this._reactiveNodes) {\n const signal = entry.signal.deref();\n if (!signal) continue;\n if (!signal.deps) continue;\n\n const visited = new Set();\n const queue = [...signal.deps];\n while (queue.length > 0) {\n const dep = queue.shift();\n if (!dep || visited.has(dep)) continue;\n visited.add(dep);\n const depId = signalToId.get(dep);\n if (depId) {\n const key = depId + '>' + nodeId;\n if (depId !== nodeId && !edgeSet.has(key)) {\n edgeSet.add(key);\n edges.push({ from: depId, to: nodeId });\n }\n } else if (dep.deps) {\n for (const d of dep.deps) queue.push(d);\n }\n }\n }\n\n return { nodes, edges };\n },\n\n sendReactiveGraph() {\n const graph = this.getReactiveGraph();\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:reactive-graph', graph);\n }\n },\n\n // --- Phase 3: State Timeline ---\n\n _pollStateValues() {\n for (const [nodeId, entry] of this._reactiveNodes) {\n if (entry.meta.type !== 'state') continue;\n if (!this._instances.has(entry.meta.componentId)) {\n this._reactiveNodes.delete(nodeId);\n continue;\n }\n const signal = entry.signal.deref();\n if (!signal || signal.v === undefined) continue;\n try {\n const currentVal = signal.v;\n const prev = this._stateSnapshots.get(nodeId);\n if (prev !== undefined && currentVal === prev) continue;\n const isPrimitive = currentVal === null || typeof currentVal !== 'object';\n let newSnapshot;\n if (isPrimitive) {\n if (prev !== undefined && prev === currentVal) continue;\n newSnapshot = currentVal;\n } else {\n const currStr = JSON.stringify(currentVal);\n const prevStr = this._stateSnapshotStrs?.get(nodeId);\n if (prevStr === currStr) continue;\n newSnapshot = JSON.parse(currStr);\n if (!this._stateSnapshotStrs) this._stateSnapshotStrs = new Map();\n this._stateSnapshotStrs.set(nodeId, currStr);\n }\n if (this._stateTimeline.length >= 500) this._stateTimeline.splice(0, this._stateTimeline.length - 499);\n this._stateTimeline.push({\n id: nodeId,\n name: entry.meta.name,\n componentFile: entry.meta.componentFile,\n oldValue: prev !== undefined ? prev : null,\n newValue: newSnapshot,\n timestamp: Date.now(),\n });\n this._stateSnapshots.set(nodeId, newSnapshot);\n this._scheduleTimelineUpdate();\n } catch { /* ignore non-serializable */ }\n }\n },\n\n _scheduleTimelineUpdate() {\n if (this._timelineDebounceTimer) clearTimeout(this._timelineDebounceTimer);\n this._timelineDebounceTimer = setTimeout(() => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: this._stateTimeline });\n }\n }, 300);\n },\n\n getStateTimeline() {\n return this._stateTimeline;\n },\n\n clearStateTimeline() {\n this._stateTimeline = [];\n this._scheduleTimelineUpdate();\n },\n\n // --- FPS Monitoring ---\n // Uses requestAnimationFrame to measure frame rate with minimal overhead.\n // Only stores timestamps - no allocations per frame beyond a single array push.\n _fpsFrameTimes: [],\n\n _fpsLoop() {\n this._fpsFrameTimes.push(performance.now());\n requestAnimationFrame(() => this._fpsLoop());\n },\n\n _sampleFps() {\n const now = performance.now();\n // Remove frame timestamps older than 1 second\n const cutoff = now - 1000;\n let i = 0;\n while (i < this._fpsFrameTimes.length && this._fpsFrameTimes[i] < cutoff) i++;\n if (i > 0) this._fpsFrameTimes.splice(0, i);\n const fps = this._fpsFrameTimes.length;\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:fps', { timestamp: Date.now(), fps });\n }\n }\n };\n\n window.__SVELTE_DEVTOOLS__ = __SVELTE_DT;\n\n // Poll state values for timeline\n setInterval(() => { __SVELTE_DT._pollStateValues(); }, 200);\n\n // FPS monitoring\n requestAnimationFrame(() => __SVELTE_DT._fpsLoop());\n setInterval(() => __SVELTE_DT._sampleFps(), 500);\n\n // Phase 3: Capture runtime errors\n window.addEventListener('error', (event) => {\n if (import.meta.hot) {\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno,\n stack: event.error?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n window.addEventListener('unhandledrejection', (event) => {\n if (import.meta.hot) {\n const reason = event.reason;\n import.meta.hot.send('svelte-devtools:runtime-error', {\n message: reason?.message || String(reason),\n stack: reason?.stack || '',\n timestamp: Date.now(),\n });\n }\n });\n\n // Listen for reactive graph requests from server\n if (import.meta.hot) {\n import.meta.hot.on('svelte-devtools:request-reactive-graph', () => {\n __SVELTE_DT.sendReactiveGraph();\n });\n import.meta.hot.on('svelte-devtools:request-state-timeline', () => {\n import.meta.hot.send('svelte-devtools:state-timeline', { changes: __SVELTE_DT._stateTimeline });\n });\n import.meta.hot.on('svelte-devtools:clear-state-timeline', () => {\n __SVELTE_DT.clearStateTimeline();\n });\n }\n}\n", R = 1, z = 5, B = 1, V = 30;
|
|
262
|
+
function H(e) {
|
|
263
|
+
return {
|
|
264
|
+
renderProfiles: e.getRenderProfiles().map((e) => ({
|
|
265
|
+
componentId: e.componentId,
|
|
266
|
+
file: e.file,
|
|
267
|
+
name: e.name,
|
|
268
|
+
renderCount: e.renderCount,
|
|
269
|
+
totalRenderTime: e.totalRenderTime
|
|
270
|
+
})),
|
|
271
|
+
loadProfilesCount: e.getLoadProfiles().length,
|
|
272
|
+
fpsCount: e.getFpsSamples().length,
|
|
273
|
+
takenAt: Date.now()
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function U(e, t, n) {
|
|
277
|
+
return Math.abs(e) < t ? "unchanged" : n ? e < 0 ? "improved" : "regressed" : e > 0 ? "improved" : "regressed";
|
|
278
|
+
}
|
|
279
|
+
var te = class {
|
|
280
|
+
sessions = /* @__PURE__ */ new Map();
|
|
281
|
+
active = null;
|
|
282
|
+
persistDir;
|
|
283
|
+
getters;
|
|
284
|
+
constructor(e) {
|
|
285
|
+
this.persistDir = e.persistDir, this.getters = e.getters;
|
|
286
|
+
}
|
|
287
|
+
start(e, t) {
|
|
288
|
+
if (this.active) throw Error(`A session is already active: ${this.active}. Call end_session before starting a new one.`);
|
|
289
|
+
let n = `s_${Date.now().toString(36)}_${r.randomBytes(3).toString("hex")}`, i = {
|
|
290
|
+
id: n,
|
|
291
|
+
label: e,
|
|
292
|
+
startedAt: Date.now(),
|
|
293
|
+
persist: t,
|
|
294
|
+
startSnapshot: H(this.getters),
|
|
295
|
+
loadProfiles: [],
|
|
296
|
+
fpsSamples: []
|
|
297
|
+
};
|
|
298
|
+
return this.sessions.set(n, i), this.active = n, i;
|
|
299
|
+
}
|
|
300
|
+
recordLoadProfile(e) {
|
|
301
|
+
if (!this.active) return;
|
|
302
|
+
let t = this.sessions.get(this.active);
|
|
303
|
+
t && t.loadProfiles.push(e);
|
|
304
|
+
}
|
|
305
|
+
recordFpsSample(e) {
|
|
306
|
+
if (!this.active) return;
|
|
307
|
+
let t = this.sessions.get(this.active);
|
|
308
|
+
t && t.fpsSamples.push(e);
|
|
309
|
+
}
|
|
310
|
+
end(e) {
|
|
311
|
+
if (!this.active) throw Error("No active session");
|
|
312
|
+
let t = this.sessions.get(this.active);
|
|
313
|
+
if (!t) throw Error("Active session missing from store");
|
|
314
|
+
return t.endedAt = Date.now(), t.endSnapshot = H(this.getters), this.active = null, e === "discard" ? this.sessions.delete(t.id) : (e === "disk" || t.persist) && this.persistToDisk(t), t;
|
|
315
|
+
}
|
|
316
|
+
get(e) {
|
|
317
|
+
return this.sessions.get(e) || this.loadFromDisk(e);
|
|
318
|
+
}
|
|
319
|
+
list() {
|
|
320
|
+
let n = /* @__PURE__ */ new Set(), r = [];
|
|
321
|
+
for (let e of this.sessions.values()) n.add(e.id), r.push({
|
|
322
|
+
id: e.id,
|
|
323
|
+
label: e.label,
|
|
324
|
+
startedAt: e.startedAt,
|
|
325
|
+
endedAt: e.endedAt,
|
|
326
|
+
persisted: t.existsSync(this.pathFor(e.id)),
|
|
327
|
+
active: this.active === e.id
|
|
328
|
+
});
|
|
329
|
+
try {
|
|
330
|
+
if (t.existsSync(this.persistDir)) for (let i of t.readdirSync(this.persistDir)) {
|
|
331
|
+
if (!i.endsWith(".json")) continue;
|
|
332
|
+
let a = i.slice(0, -5);
|
|
333
|
+
if (!n.has(a)) try {
|
|
334
|
+
let n = JSON.parse(t.readFileSync(e.join(this.persistDir, i), "utf-8"));
|
|
335
|
+
r.push({
|
|
336
|
+
id: n.id,
|
|
337
|
+
label: n.label,
|
|
338
|
+
startedAt: n.startedAt,
|
|
339
|
+
endedAt: n.endedAt,
|
|
340
|
+
persisted: !0,
|
|
341
|
+
active: !1
|
|
342
|
+
});
|
|
343
|
+
} catch {}
|
|
344
|
+
}
|
|
345
|
+
} catch {}
|
|
346
|
+
return r.sort((e, t) => t.startedAt - e.startedAt);
|
|
347
|
+
}
|
|
348
|
+
delete(e) {
|
|
349
|
+
let n = this.sessions.delete(e), r = !1;
|
|
350
|
+
try {
|
|
351
|
+
let n = this.pathFor(e);
|
|
352
|
+
t.existsSync(n) && (t.unlinkSync(n), r = !0);
|
|
353
|
+
} catch {}
|
|
354
|
+
return this.active === e && (this.active = null), n || r;
|
|
355
|
+
}
|
|
356
|
+
delta(e) {
|
|
357
|
+
let t = this.get(e);
|
|
358
|
+
if (!t) throw Error(`Session not found: ${e}`);
|
|
359
|
+
if (!t.endSnapshot || t.endedAt === void 0) throw Error(`Session ${e} has not been ended yet`);
|
|
360
|
+
let n = new Map(t.startSnapshot.renderProfiles.map((e) => [e.componentId, e])), r = t.endSnapshot.renderProfiles.map((e) => {
|
|
361
|
+
let t = n.get(e.componentId), r = t?.renderCount ?? 0, i = t?.totalRenderTime ?? 0, a = e.renderCount - r, o = e.totalRenderTime - i;
|
|
362
|
+
return {
|
|
363
|
+
componentId: e.componentId,
|
|
364
|
+
file: e.file,
|
|
365
|
+
name: e.name,
|
|
366
|
+
renderCountDelta: a,
|
|
367
|
+
totalRenderTimeDelta: o,
|
|
368
|
+
avgRenderTimeDelta: a > 0 ? o / a : 0
|
|
369
|
+
};
|
|
370
|
+
}).filter((e) => e.renderCountDelta > 0).sort((e, t) => t.totalRenderTimeDelta - e.totalRenderTimeDelta), i = t.loadProfiles.map((e) => e.duration), a = W(i), o = ne(i, 95), s = t.fpsSamples.map((e) => e.fps), c = W(s), l = s.length ? Math.min(...s) : 0, u = s.filter((e) => e < V).length;
|
|
371
|
+
return {
|
|
372
|
+
durationMs: t.endedAt - t.startedAt,
|
|
373
|
+
components: r,
|
|
374
|
+
loadProfiles: {
|
|
375
|
+
count: t.loadProfiles.length,
|
|
376
|
+
avgDuration: a,
|
|
377
|
+
p95Duration: o
|
|
378
|
+
},
|
|
379
|
+
fps: {
|
|
380
|
+
samples: s.length,
|
|
381
|
+
avg: c,
|
|
382
|
+
min: l,
|
|
383
|
+
drops: u
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
compare(e, t) {
|
|
388
|
+
let n = this.delta(e), r = this.delta(t), i = this.get(e), a = this.get(t), o = n.components.reduce((e, t) => e + t.totalRenderTimeDelta, 0), s = r.components.reduce((e, t) => e + t.totalRenderTimeDelta, 0), c = s - o, l = r.loadProfiles.avgDuration - n.loadProfiles.avgDuration, u = r.fps.avg - n.fps.avg;
|
|
389
|
+
return {
|
|
390
|
+
a: {
|
|
391
|
+
id: i.id,
|
|
392
|
+
label: i.label
|
|
393
|
+
},
|
|
394
|
+
b: {
|
|
395
|
+
id: a.id,
|
|
396
|
+
label: a.label
|
|
397
|
+
},
|
|
398
|
+
render: {
|
|
399
|
+
totalRenderTimeDeltaA: o,
|
|
400
|
+
totalRenderTimeDeltaB: s,
|
|
401
|
+
diff: c,
|
|
402
|
+
verdict: U(c, R, !0)
|
|
403
|
+
},
|
|
404
|
+
load: {
|
|
405
|
+
avgA: n.loadProfiles.avgDuration,
|
|
406
|
+
avgB: r.loadProfiles.avgDuration,
|
|
407
|
+
diff: l,
|
|
408
|
+
verdict: U(l, z, !0)
|
|
409
|
+
},
|
|
410
|
+
fps: {
|
|
411
|
+
avgA: n.fps.avg,
|
|
412
|
+
avgB: r.fps.avg,
|
|
413
|
+
diff: u,
|
|
414
|
+
verdict: U(u, B, !1)
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
pathFor(t) {
|
|
419
|
+
return e.join(this.persistDir, `${t}.json`);
|
|
420
|
+
}
|
|
421
|
+
persistToDisk(e) {
|
|
422
|
+
try {
|
|
423
|
+
t.mkdirSync(this.persistDir, { recursive: !0 }), t.writeFileSync(this.pathFor(e.id), JSON.stringify(e, null, 2), { mode: 384 });
|
|
424
|
+
} catch {}
|
|
425
|
+
}
|
|
426
|
+
loadFromDisk(e) {
|
|
427
|
+
try {
|
|
428
|
+
let n = t.readFileSync(this.pathFor(e), "utf-8");
|
|
429
|
+
return JSON.parse(n);
|
|
430
|
+
} catch {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
function W(e) {
|
|
436
|
+
return e.length ? e.reduce((e, t) => e + t, 0) / e.length : 0;
|
|
437
|
+
}
|
|
438
|
+
function ne(e, t) {
|
|
439
|
+
if (!e.length) return 0;
|
|
440
|
+
let n = [...e].sort((e, t) => e - t);
|
|
441
|
+
return n[Math.min(n.length - 1, Math.floor(t / 100 * n.length))];
|
|
442
|
+
}
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/mcp/issues.ts
|
|
445
|
+
var G = {
|
|
446
|
+
avgRenderTimeMs: 4,
|
|
447
|
+
renderCount: 30,
|
|
448
|
+
loadDurationMs: 200,
|
|
449
|
+
fpsDropThreshold: 40,
|
|
450
|
+
effectMaxDeps: 8
|
|
451
|
+
};
|
|
452
|
+
function re(e, t = {}) {
|
|
453
|
+
let n = {
|
|
454
|
+
...G,
|
|
455
|
+
...t
|
|
456
|
+
}, r = [];
|
|
457
|
+
for (let t of e.renderProfiles) {
|
|
458
|
+
let e = t.renderCount > 0 ? t.totalRenderTime / t.renderCount : 0;
|
|
459
|
+
e >= n.avgRenderTimeMs && r.push({
|
|
460
|
+
id: `slow-render:${t.file}:${t.componentId}`,
|
|
461
|
+
kind: "slow-component-render",
|
|
462
|
+
severity: e >= n.avgRenderTimeMs * 4 ? "high" : e >= n.avgRenderTimeMs * 2 ? "medium" : "low",
|
|
463
|
+
summary: `${t.name} averages ${e.toFixed(2)}ms per render (${t.renderCount} renders)`,
|
|
464
|
+
file: t.file,
|
|
465
|
+
metric: {
|
|
466
|
+
avgRenderTimeMs: q(e),
|
|
467
|
+
renderCount: t.renderCount,
|
|
468
|
+
totalRenderTimeMs: q(t.totalRenderTime)
|
|
469
|
+
},
|
|
470
|
+
suggestedTool: "get_render_profile"
|
|
471
|
+
}), t.renderCount >= n.renderCount && r.push({
|
|
472
|
+
id: `over-render:${t.file}:${t.componentId}`,
|
|
473
|
+
kind: "over-rendered-component",
|
|
474
|
+
severity: t.renderCount >= n.renderCount * 8 ? "high" : t.renderCount >= n.renderCount * 3 ? "medium" : "low",
|
|
475
|
+
summary: `${t.name} rendered ${t.renderCount} times`,
|
|
476
|
+
file: t.file,
|
|
477
|
+
metric: {
|
|
478
|
+
renderCount: t.renderCount,
|
|
479
|
+
avgRenderTimeMs: q(e)
|
|
480
|
+
},
|
|
481
|
+
suggestedTool: "get_component_hotspots"
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
for (let t of e.loadProfiles) t.duration >= n.loadDurationMs && r.push({
|
|
485
|
+
id: `slow-load:${t.route}:${t.timestamp}`,
|
|
486
|
+
kind: "slow-load",
|
|
487
|
+
severity: t.duration >= n.loadDurationMs * 5 ? "high" : t.duration >= n.loadDurationMs * 2 ? "medium" : "low",
|
|
488
|
+
summary: `${t.type} load for ${t.route} took ${t.duration.toFixed(0)}ms`,
|
|
489
|
+
file: t.file,
|
|
490
|
+
metric: {
|
|
491
|
+
durationMs: q(t.duration),
|
|
492
|
+
dataSizeBytes: t.dataSize
|
|
493
|
+
},
|
|
494
|
+
suggestedTool: "get_load_waterfall"
|
|
495
|
+
});
|
|
496
|
+
let i = e.fpsSamples.filter((e) => e.fps < n.fpsDropThreshold);
|
|
497
|
+
if (i.length > 0) {
|
|
498
|
+
let t = Math.min(...i.map((e) => e.fps));
|
|
499
|
+
r.push({
|
|
500
|
+
id: `fps-drops:${e.fpsSamples[0]?.timestamp ?? 0}`,
|
|
501
|
+
kind: "fps-drop",
|
|
502
|
+
severity: t < 15 ? "high" : t < 30 ? "medium" : "low",
|
|
503
|
+
summary: `${i.length} FPS samples below ${n.fpsDropThreshold} (min ${t})`,
|
|
504
|
+
metric: {
|
|
505
|
+
dropCount: i.length,
|
|
506
|
+
minFps: t,
|
|
507
|
+
threshold: n.fpsDropThreshold
|
|
508
|
+
},
|
|
509
|
+
suggestedTool: "get_fps_drops"
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
let a = /* @__PURE__ */ new Map();
|
|
513
|
+
for (let t of e.reactiveGraph.edges) a.set(t.from, (a.get(t.from) ?? 0) + 1);
|
|
514
|
+
let o = /* @__PURE__ */ new Map();
|
|
515
|
+
for (let t of e.reactiveGraph.edges) o.set(t.to, (o.get(t.to) ?? 0) + 1);
|
|
516
|
+
for (let t of e.reactiveGraph.nodes) {
|
|
517
|
+
if (t.type === "effect") {
|
|
518
|
+
let e = o.get(t.id) ?? 0;
|
|
519
|
+
e >= n.effectMaxDeps && r.push({
|
|
520
|
+
id: `effect-deps:${t.id}`,
|
|
521
|
+
kind: "effect-overconnected",
|
|
522
|
+
severity: e >= n.effectMaxDeps * 3 ? "high" : e >= n.effectMaxDeps * 2 ? "medium" : "low",
|
|
523
|
+
summary: `effect "${t.name}" depends on ${e} reactive values`,
|
|
524
|
+
file: t.componentFile,
|
|
525
|
+
metric: { depCount: e },
|
|
526
|
+
suggestedTool: "get_reactive_graph_problems"
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if (t.type === "derived") {
|
|
530
|
+
let e = a.get(t.id) ?? 0;
|
|
531
|
+
e === 0 && (o.get(t.id) ?? 0) > 0 && r.push({
|
|
532
|
+
id: `derived-orphan:${t.id}`,
|
|
533
|
+
kind: "derived-orphan",
|
|
534
|
+
severity: "low",
|
|
535
|
+
summary: `derived "${t.name}" has dependencies but is unused`,
|
|
536
|
+
file: t.componentFile,
|
|
537
|
+
metric: { fanout: e },
|
|
538
|
+
suggestedTool: "get_reactive_graph_problems"
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return r.sort(ie);
|
|
543
|
+
}
|
|
544
|
+
var K = {
|
|
545
|
+
high: 0,
|
|
546
|
+
medium: 1,
|
|
547
|
+
low: 2
|
|
548
|
+
};
|
|
549
|
+
function ie(e, t) {
|
|
550
|
+
return K[e.severity] - K[t.severity];
|
|
551
|
+
}
|
|
552
|
+
function q(e) {
|
|
553
|
+
return Math.round(e * 100) / 100;
|
|
554
|
+
}
|
|
555
|
+
function ae(e, t = {}) {
|
|
556
|
+
let n = {
|
|
557
|
+
...G,
|
|
558
|
+
...t
|
|
559
|
+
}, r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
|
|
560
|
+
for (let t of e.edges) r.set(t.to, (r.get(t.to) ?? 0) + 1), i.set(t.from, (i.get(t.from) ?? 0) + 1);
|
|
561
|
+
let a = [], o = [], s = [];
|
|
562
|
+
for (let t of e.nodes) {
|
|
563
|
+
let e = r.get(t.id) ?? 0, c = i.get(t.id) ?? 0;
|
|
564
|
+
t.type === "effect" && e >= n.effectMaxDeps && a.push({
|
|
565
|
+
id: t.id,
|
|
566
|
+
name: t.name,
|
|
567
|
+
file: t.componentFile,
|
|
568
|
+
depCount: e
|
|
569
|
+
}), t.type === "derived" && c === 0 && e > 0 && o.push({
|
|
570
|
+
id: t.id,
|
|
571
|
+
name: t.name,
|
|
572
|
+
file: t.componentFile
|
|
573
|
+
}), e === 0 && c === 0 && s.push({
|
|
574
|
+
id: t.id,
|
|
575
|
+
name: t.name,
|
|
576
|
+
type: t.type,
|
|
577
|
+
file: t.componentFile
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
effects: a,
|
|
582
|
+
orphanDeriveds: o,
|
|
583
|
+
isolatedNodes: s
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/mcp/server.ts
|
|
588
|
+
var J = (e) => ({ content: [{
|
|
589
|
+
type: "text",
|
|
590
|
+
text: typeof e == "string" ? e : JSON.stringify(e, null, 2)
|
|
591
|
+
}] });
|
|
592
|
+
function oe(e) {
|
|
593
|
+
let t = new c({
|
|
594
|
+
name: "vite-devtools-svelte",
|
|
595
|
+
version: "0.1.0"
|
|
596
|
+
});
|
|
597
|
+
return t.registerTool("list_performance_issues", {
|
|
598
|
+
title: "List performance issues",
|
|
599
|
+
description: "Cross-cuts render/reactive/load/fps metrics and returns ranked issues. The entry point for AI-driven performance audits. Each issue carries `suggestedTool` naming the detail tool to call next.",
|
|
600
|
+
inputSchema: {
|
|
601
|
+
avgRenderTimeMs: u.number().optional(),
|
|
602
|
+
renderCount: u.number().optional(),
|
|
603
|
+
loadDurationMs: u.number().optional(),
|
|
604
|
+
fpsDropThreshold: u.number().optional(),
|
|
605
|
+
effectMaxDeps: u.number().optional()
|
|
606
|
+
}
|
|
607
|
+
}, async (t) => {
|
|
608
|
+
let n = t, r = await e.getReactiveGraph(), i = re({
|
|
609
|
+
renderProfiles: e.getRenderProfiles(),
|
|
610
|
+
reactiveGraph: r,
|
|
611
|
+
loadProfiles: e.getLoadProfiles(),
|
|
612
|
+
fpsSamples: e.getFpsSamples()
|
|
613
|
+
}, n);
|
|
614
|
+
return J({
|
|
615
|
+
count: i.length,
|
|
616
|
+
issues: i
|
|
617
|
+
});
|
|
618
|
+
}), t.registerTool("get_component_hotspots", {
|
|
619
|
+
title: "Component render hotspots",
|
|
620
|
+
description: "Top components by total render time, with render count and per-render average.",
|
|
621
|
+
inputSchema: { topN: u.number().int().min(1).max(200).optional() }
|
|
622
|
+
}, async ({ topN: t = 20 }) => J([...e.getRenderProfiles()].map((e) => ({
|
|
623
|
+
file: e.file,
|
|
624
|
+
name: e.name,
|
|
625
|
+
componentId: e.componentId,
|
|
626
|
+
renderCount: e.renderCount,
|
|
627
|
+
totalRenderTimeMs: Y(e.totalRenderTime),
|
|
628
|
+
avgRenderTimeMs: Y(e.renderCount > 0 ? e.totalRenderTime / e.renderCount : 0),
|
|
629
|
+
lastRenderTimeMs: Y(e.lastRenderTime),
|
|
630
|
+
lastRenderAt: e.lastRenderAt
|
|
631
|
+
})).sort((e, t) => t.totalRenderTimeMs - e.totalRenderTimeMs).slice(0, t))), t.registerTool("get_reactive_graph_problems", {
|
|
632
|
+
title: "Reactive graph problems",
|
|
633
|
+
description: "Classified reactive graph issues: over-connected effects, orphan deriveds, isolated nodes. Returns categories instead of the full graph.",
|
|
634
|
+
inputSchema: { effectMaxDeps: u.number().int().min(1).optional() }
|
|
635
|
+
}, async ({ effectMaxDeps: t }) => J(ae(await e.getReactiveGraph(), { effectMaxDeps: t }))), t.registerTool("get_load_waterfall", {
|
|
636
|
+
title: "SvelteKit load waterfall",
|
|
637
|
+
description: "Load profiles grouped by route, with timing and data size. Optionally filtered by route.",
|
|
638
|
+
inputSchema: { route: u.string().optional() }
|
|
639
|
+
}, async ({ route: t }) => {
|
|
640
|
+
let n = e.getLoadProfiles();
|
|
641
|
+
t && (n = n.filter((e) => e.route === t));
|
|
642
|
+
let r = /* @__PURE__ */ new Map();
|
|
643
|
+
for (let e of n) {
|
|
644
|
+
let t = r.get(e.route) ?? [];
|
|
645
|
+
t.push(e), r.set(e.route, t);
|
|
646
|
+
}
|
|
647
|
+
return J([...r.entries()].map(([e, t]) => {
|
|
648
|
+
let n = t.map((e) => e.duration);
|
|
649
|
+
return {
|
|
650
|
+
route: e,
|
|
651
|
+
file: t[0]?.file,
|
|
652
|
+
count: t.length,
|
|
653
|
+
avgDuration: Y(se(n)),
|
|
654
|
+
maxDuration: Y(Math.max(...n)),
|
|
655
|
+
totalDataBytes: t.reduce((e, t) => e + t.dataSize, 0),
|
|
656
|
+
samples: t
|
|
657
|
+
};
|
|
658
|
+
}).sort((e, t) => t.avgDuration - e.avgDuration));
|
|
659
|
+
}), t.registerTool("get_fps_drops", {
|
|
660
|
+
title: "FPS drops",
|
|
661
|
+
description: "Samples whose FPS fell below `threshold`. Returns timestamp + fps for each.",
|
|
662
|
+
inputSchema: {
|
|
663
|
+
threshold: u.number().min(1).max(120).optional(),
|
|
664
|
+
sinceMs: u.number().optional()
|
|
665
|
+
}
|
|
666
|
+
}, async ({ threshold: t = 40, sinceMs: n }) => {
|
|
667
|
+
let r = e.getFpsSamples();
|
|
668
|
+
if (n !== void 0) {
|
|
669
|
+
let e = Date.now() - n;
|
|
670
|
+
r = r.filter((t) => t.timestamp >= e);
|
|
671
|
+
}
|
|
672
|
+
let i = r.filter((e) => e.fps < t);
|
|
673
|
+
return J({
|
|
674
|
+
threshold: t,
|
|
675
|
+
sampleCount: r.length,
|
|
676
|
+
dropCount: i.length,
|
|
677
|
+
minFps: i.length ? Math.min(...i.map((e) => e.fps)) : null,
|
|
678
|
+
drops: i
|
|
679
|
+
});
|
|
680
|
+
}), t.registerTool("get_render_profile", {
|
|
681
|
+
title: "Render profile for a specific file",
|
|
682
|
+
description: "Returns render profile entries matching the given component file (substring match).",
|
|
683
|
+
inputSchema: { file: u.string() }
|
|
684
|
+
}, async ({ file: t }) => J(e.getRenderProfiles().filter((e) => e.file.includes(t)).map((e) => ({
|
|
685
|
+
...e,
|
|
686
|
+
totalRenderTimeMs: Y(e.totalRenderTime),
|
|
687
|
+
avgRenderTimeMs: Y(e.renderCount > 0 ? e.totalRenderTime / e.renderCount : 0)
|
|
688
|
+
})))), t.registerTool("get_project_info", {
|
|
689
|
+
title: "Project info",
|
|
690
|
+
description: "Package name/version, Svelte / SvelteKit / Vite versions, dependency lists.",
|
|
691
|
+
inputSchema: {}
|
|
692
|
+
}, async () => J(e.getProject())), t.registerTool("get_routes", {
|
|
693
|
+
title: "SvelteKit routes",
|
|
694
|
+
description: "Static analysis of the SvelteKit routes tree.",
|
|
695
|
+
inputSchema: {}
|
|
696
|
+
}, async () => J(e.getRoutes())), t.registerTool("get_live_components", {
|
|
697
|
+
title: "Currently mounted components",
|
|
698
|
+
description: "Component instances with file, parent, mounted status as currently mounted in the browser.",
|
|
699
|
+
inputSchema: {}
|
|
700
|
+
}, async () => J(e.getLiveComponents())), t.registerTool("get_component_relations", {
|
|
701
|
+
title: "Component import graph",
|
|
702
|
+
description: "Static import relations between .svelte components.",
|
|
703
|
+
inputSchema: {}
|
|
704
|
+
}, async () => J(e.getComponentRelations())), t.registerTool("start_session", {
|
|
705
|
+
title: "Start measurement session",
|
|
706
|
+
description: "Begin capturing metrics under a labelled session. Required before compare_sessions. Set `persist:true` to write to disk on end; otherwise the session lives in memory only.",
|
|
707
|
+
inputSchema: {
|
|
708
|
+
label: u.string(),
|
|
709
|
+
persist: u.boolean().optional()
|
|
710
|
+
}
|
|
711
|
+
}, async ({ label: t, persist: n = !1 }) => {
|
|
712
|
+
let r = e.sessions.start(t, n);
|
|
713
|
+
return J({
|
|
714
|
+
id: r.id,
|
|
715
|
+
label: r.label,
|
|
716
|
+
startedAt: r.startedAt,
|
|
717
|
+
persist: r.persist
|
|
718
|
+
});
|
|
719
|
+
}), t.registerTool("end_session", {
|
|
720
|
+
title: "End current measurement session",
|
|
721
|
+
description: "Closes the active session and returns its delta. `keep` decides disposal: \"memory\" keeps it for this dev-server lifetime, \"disk\" writes to .vite-devtools-svelte/sessions/, \"discard\" deletes it.",
|
|
722
|
+
inputSchema: { keep: u.enum([
|
|
723
|
+
"memory",
|
|
724
|
+
"disk",
|
|
725
|
+
"discard"
|
|
726
|
+
]).optional() }
|
|
727
|
+
}, async ({ keep: t = "memory" }) => {
|
|
728
|
+
let n = e.sessions.end(t), r = n.endedAt ? e.sessions.delta(n.id) : null;
|
|
729
|
+
return J({
|
|
730
|
+
id: n.id,
|
|
731
|
+
label: n.label,
|
|
732
|
+
startedAt: n.startedAt,
|
|
733
|
+
endedAt: n.endedAt,
|
|
734
|
+
keep: t,
|
|
735
|
+
delta: r
|
|
736
|
+
});
|
|
737
|
+
}), t.registerTool("compare_sessions", {
|
|
738
|
+
title: "Compare two sessions",
|
|
739
|
+
description: "Diff render / load / fps metrics between two ended sessions. Each section carries `verdict`: improved | regressed | unchanged.",
|
|
740
|
+
inputSchema: {
|
|
741
|
+
a: u.string(),
|
|
742
|
+
b: u.string()
|
|
743
|
+
}
|
|
744
|
+
}, async ({ a: t, b: n }) => J(e.sessions.compare(t, n))), t.registerTool("list_sessions", {
|
|
745
|
+
title: "List sessions",
|
|
746
|
+
description: "In-memory + on-disk sessions, most recent first.",
|
|
747
|
+
inputSchema: {}
|
|
748
|
+
}, async () => J(e.sessions.list())), t.registerTool("load_session", {
|
|
749
|
+
title: "Load a session by id",
|
|
750
|
+
description: "Returns the full session record (including delta if ended) by id.",
|
|
751
|
+
inputSchema: { id: u.string() }
|
|
752
|
+
}, async ({ id: t }) => {
|
|
753
|
+
let n = e.sessions.get(t);
|
|
754
|
+
if (!n) return J({ error: `Session not found: ${t}` });
|
|
755
|
+
let r = n.endedAt ? e.sessions.delta(t) : null;
|
|
756
|
+
return J({
|
|
757
|
+
...n,
|
|
758
|
+
delta: r
|
|
759
|
+
});
|
|
760
|
+
}), t.registerTool("delete_session", {
|
|
761
|
+
title: "Delete a session",
|
|
762
|
+
description: "Removes a session from memory and disk.",
|
|
763
|
+
inputSchema: { id: u.string() }
|
|
764
|
+
}, async ({ id: t }) => J({ deleted: e.sessions.delete(t) })), t;
|
|
765
|
+
}
|
|
766
|
+
function se(e) {
|
|
767
|
+
return e.length ? e.reduce((e, t) => e + t, 0) / e.length : 0;
|
|
768
|
+
}
|
|
769
|
+
function Y(e) {
|
|
770
|
+
return Math.round(e * 100) / 100;
|
|
771
|
+
}
|
|
772
|
+
var ce = "templates: {", X = "/@id/@vitejs/devtools/client/inject", le = `<script type=\\"module\\" src=\\"${X}\\"><\/script>`, Z = "</body>";
|
|
773
|
+
function ue(e) {
|
|
774
|
+
if (!e.includes("templates: {") || !e.includes(Z) || e.includes(X)) return null;
|
|
775
|
+
let t = e.indexOf(ce), n = e.indexOf(Z, t);
|
|
776
|
+
return n === -1 ? null : e.slice(0, n) + le + e.slice(n);
|
|
777
|
+
}
|
|
778
|
+
function de() {
|
|
779
|
+
return {
|
|
780
|
+
name: "vite-devtools-svelte:sveltekit-template-injector",
|
|
781
|
+
enforce: "post",
|
|
782
|
+
apply: (e, t) => t.command === "serve" && !t.isSsrBuild,
|
|
783
|
+
transform(e, t) {
|
|
784
|
+
if (!t.endsWith(".svelte-kit/generated/server/internal.js")) return;
|
|
785
|
+
let n = ue(e);
|
|
786
|
+
if (n !== null) return {
|
|
787
|
+
code: n,
|
|
788
|
+
map: null
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
//#endregion
|
|
794
|
+
//#region src/plugin.ts
|
|
795
|
+
var Q = e.dirname(i(import.meta.url));
|
|
796
|
+
function $(e) {
|
|
260
797
|
let t;
|
|
261
798
|
try {
|
|
262
799
|
t = new URL(e);
|
|
@@ -267,21 +804,21 @@ function L(e) {
|
|
|
267
804
|
let r = t.hostname;
|
|
268
805
|
if (r === "::1" || r === "[::1]") throw Error("Blocked: loopback address");
|
|
269
806
|
if (n.isIP(r)) {
|
|
270
|
-
if (
|
|
807
|
+
if (fe(r)) throw Error(`Blocked: private IP address ${r}`);
|
|
271
808
|
} else {
|
|
272
809
|
let e = r.toLowerCase();
|
|
273
810
|
if (e === "localhost" || e.endsWith(".local") || e.endsWith(".internal")) throw Error(`Blocked: internal hostname ${r}`);
|
|
274
811
|
}
|
|
275
812
|
}
|
|
276
|
-
function
|
|
813
|
+
function fe(e) {
|
|
277
814
|
let t = e.split(".").map(Number);
|
|
278
815
|
return !!(t.length === 4 && (t[0] === 127 || t[0] === 10 || t[0] === 172 && t[1] >= 16 && t[1] <= 31 || t[0] === 192 && t[1] === 168 || t[0] === 169 && t[1] === 254 || t.every((e) => e === 0)));
|
|
279
816
|
}
|
|
280
|
-
function
|
|
281
|
-
let { componentTracking: i = !0 } = n, o, s,
|
|
282
|
-
function
|
|
817
|
+
function pe(n = {}) {
|
|
818
|
+
let { componentTracking: i = !0 } = n, o, s, c, u = r.randomUUID();
|
|
819
|
+
function f(n) {
|
|
283
820
|
if (typeof n != "string" || n.length === 0) throw Error("Invalid file path");
|
|
284
|
-
let r = t.realpathSync(
|
|
821
|
+
let r = t.realpathSync(c), i = e.isAbsolute(n) ? n : e.resolve(c, n), a;
|
|
285
822
|
try {
|
|
286
823
|
a = t.realpathSync(i);
|
|
287
824
|
} catch {
|
|
@@ -290,23 +827,34 @@ function z(n = {}) {
|
|
|
290
827
|
if (a !== r && !a.startsWith(r + e.sep)) throw Error("Forbidden: path outside project root");
|
|
291
828
|
return a;
|
|
292
829
|
}
|
|
293
|
-
let
|
|
830
|
+
let p = [], m = [], h = [], v = {
|
|
294
831
|
nodes: [],
|
|
295
832
|
edges: []
|
|
296
|
-
},
|
|
833
|
+
}, y = [], b = [], x = [], S = [], w = [], T = [], E = null;
|
|
297
834
|
function D() {
|
|
298
|
-
return E
|
|
299
|
-
"
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
835
|
+
return E ||= new te({
|
|
836
|
+
persistDir: e.join(c, "node_modules", ".vite-devtools-svelte", "sessions"),
|
|
837
|
+
getters: {
|
|
838
|
+
getRenderProfiles: () => m,
|
|
839
|
+
getLoadProfiles: () => h,
|
|
840
|
+
getFpsSamples: () => T
|
|
841
|
+
}
|
|
842
|
+
}), E;
|
|
843
|
+
}
|
|
844
|
+
let k = null;
|
|
845
|
+
function A() {
|
|
846
|
+
return k || (k = {
|
|
847
|
+
"svelte-devtools:get-project": async () => C(c),
|
|
848
|
+
"svelte-devtools:get-routes": async () => d(C(c).routesDir),
|
|
849
|
+
"svelte-devtools:get-assets": async () => _(C(c).staticDir),
|
|
850
|
+
"svelte-devtools:get-component-relations": async () => O(c),
|
|
851
|
+
"svelte-devtools:get-live-components": async () => p,
|
|
304
852
|
"svelte-devtools:open-in-editor": async (e, t) => {
|
|
305
|
-
let n =
|
|
853
|
+
let n = f(String(e));
|
|
306
854
|
t && Number(t) > 0 ? a("code", ["--goto", `${n}:${Number(t)}`]) : a("code", [n]);
|
|
307
855
|
},
|
|
308
856
|
"svelte-devtools:open-reactive-in-editor": async (e, n, r) => {
|
|
309
|
-
let i =
|
|
857
|
+
let i = f(String(e)), o = 0;
|
|
310
858
|
try {
|
|
311
859
|
let e = t.readFileSync(i, "utf-8").split("\n");
|
|
312
860
|
if (String(r) === "effect") {
|
|
@@ -324,20 +872,20 @@ function z(n = {}) {
|
|
|
324
872
|
} catch {}
|
|
325
873
|
o > 0 ? a("code", ["--goto", `${i}:${o}`]) : a("code", [i]);
|
|
326
874
|
},
|
|
327
|
-
"svelte-devtools:get-render-profiles": async () =>
|
|
875
|
+
"svelte-devtools:get-render-profiles": async () => m,
|
|
328
876
|
"svelte-devtools:get-reactive-graph": async () => s ? (s.hot.send("svelte-devtools:request-reactive-graph", {}), new Promise((e) => {
|
|
329
877
|
let t = !1, n = (n) => {
|
|
330
878
|
t || (t = !0, clearTimeout(r), e(n));
|
|
331
879
|
}, r = setTimeout(() => {
|
|
332
880
|
t = !0;
|
|
333
|
-
let r =
|
|
334
|
-
r !== -1 &&
|
|
881
|
+
let r = y.indexOf(n);
|
|
882
|
+
r !== -1 && y.splice(r, 1), e(v);
|
|
335
883
|
}, 1e3);
|
|
336
|
-
|
|
337
|
-
})) :
|
|
338
|
-
"svelte-devtools:get-load-profiles": async () =>
|
|
884
|
+
y.push(n);
|
|
885
|
+
})) : v,
|
|
886
|
+
"svelte-devtools:get-load-profiles": async () => h,
|
|
339
887
|
"svelte-devtools:clear-load-profiles": async () => {
|
|
340
|
-
|
|
888
|
+
h = [];
|
|
341
889
|
},
|
|
342
890
|
"svelte-devtools:get-state-timeline": async () => s ? (s.hot.send("svelte-devtools:request-state-timeline", {}), new Promise((e) => {
|
|
343
891
|
let t = !1, n = (n) => {
|
|
@@ -345,15 +893,15 @@ function z(n = {}) {
|
|
|
345
893
|
}, r = setTimeout(() => {
|
|
346
894
|
t = !0;
|
|
347
895
|
let r = x.indexOf(n);
|
|
348
|
-
r !== -1 && x.splice(r, 1), e(
|
|
896
|
+
r !== -1 && x.splice(r, 1), e(b);
|
|
349
897
|
}, 1e3);
|
|
350
898
|
x.push(n);
|
|
351
|
-
})) :
|
|
899
|
+
})) : b,
|
|
352
900
|
"svelte-devtools:clear-state-timeline": async () => {
|
|
353
|
-
|
|
901
|
+
b = [], s?.hot.send("svelte-devtools:clear-state-timeline", {});
|
|
354
902
|
},
|
|
355
903
|
"svelte-devtools:get-api-endpoints": async () => {
|
|
356
|
-
let e =
|
|
904
|
+
let e = d(C(c).routesDir), n = [];
|
|
357
905
|
for (let r of e) {
|
|
358
906
|
let e = r.files.find((e) => e.type === "endpoint");
|
|
359
907
|
if (e) {
|
|
@@ -382,7 +930,7 @@ function z(n = {}) {
|
|
|
382
930
|
"svelte-devtools:send-api-request": async (e, t, n, r) => {
|
|
383
931
|
let i = performance.now();
|
|
384
932
|
try {
|
|
385
|
-
|
|
933
|
+
$(String(e));
|
|
386
934
|
let a = n ? JSON.parse(String(n)) : {}, o = {
|
|
387
935
|
method: String(t),
|
|
388
936
|
headers: a
|
|
@@ -409,18 +957,18 @@ function z(n = {}) {
|
|
|
409
957
|
}
|
|
410
958
|
},
|
|
411
959
|
"svelte-devtools:get-compiler-warnings": async () => S,
|
|
412
|
-
"svelte-devtools:get-runtime-errors": async () =>
|
|
960
|
+
"svelte-devtools:get-runtime-errors": async () => w,
|
|
413
961
|
"svelte-devtools:clear-errors": async () => {
|
|
414
|
-
S = [],
|
|
962
|
+
S = [], w = [];
|
|
415
963
|
},
|
|
416
|
-
"svelte-devtools:get-svelte-files": async () =>
|
|
964
|
+
"svelte-devtools:get-svelte-files": async () => O(c).map((e) => ({
|
|
417
965
|
file: e.file,
|
|
418
966
|
name: e.name
|
|
419
967
|
})),
|
|
420
968
|
"svelte-devtools:inspect-file": async (e) => {
|
|
421
969
|
let n = String(e), r;
|
|
422
970
|
try {
|
|
423
|
-
r =
|
|
971
|
+
r = f(n);
|
|
424
972
|
} catch {
|
|
425
973
|
return {
|
|
426
974
|
source: "",
|
|
@@ -456,52 +1004,52 @@ function z(n = {}) {
|
|
|
456
1004
|
sources: c
|
|
457
1005
|
};
|
|
458
1006
|
},
|
|
459
|
-
"svelte-devtools:get-module-graph": async () =>
|
|
460
|
-
"svelte-devtools:get-og-preview": async (e) =>
|
|
1007
|
+
"svelte-devtools:get-module-graph": async () => M(),
|
|
1008
|
+
"svelte-devtools:get-og-preview": async (e) => N(String(e)),
|
|
461
1009
|
"svelte-devtools:get-build-analysis": async () => R(),
|
|
462
|
-
"svelte-devtools:get-fps": async () =>
|
|
1010
|
+
"svelte-devtools:get-fps": async () => T,
|
|
463
1011
|
"svelte-devtools:clear-fps": async () => {
|
|
464
|
-
|
|
1012
|
+
T = [];
|
|
465
1013
|
}
|
|
466
|
-
},
|
|
1014
|
+
}, k);
|
|
467
1015
|
}
|
|
468
|
-
let
|
|
1016
|
+
let j = {
|
|
469
1017
|
name: "vite-devtools-svelte",
|
|
470
1018
|
enforce: "pre",
|
|
471
1019
|
api: { getDevtoolsToken: () => u },
|
|
472
1020
|
configResolved(e) {
|
|
473
|
-
o = e,
|
|
1021
|
+
o = e, c = o.root;
|
|
474
1022
|
},
|
|
475
1023
|
configureServer(n) {
|
|
476
1024
|
s = n;
|
|
477
|
-
let r = 5e3, i =
|
|
1025
|
+
let r = 5e3, i = 2e4;
|
|
478
1026
|
s.hot.on("svelte-devtools:components", (e) => {
|
|
479
1027
|
let t = Array.isArray(e?.components) ? e.components : [];
|
|
480
|
-
|
|
1028
|
+
p = t.length > 5e3 ? t.slice(-5e3) : t;
|
|
481
1029
|
}), s.hot.on("svelte-devtools:profiles", (e) => {
|
|
482
1030
|
let t = Array.isArray(e?.profiles) ? e.profiles : [];
|
|
483
|
-
|
|
1031
|
+
m = t.length > 5e3 ? t.slice(-5e3) : t;
|
|
484
1032
|
}), s.hot.on("svelte-devtools:state-timeline", (e) => {
|
|
485
1033
|
let t = Array.isArray(e?.changes) ? e.changes : [];
|
|
486
|
-
|
|
1034
|
+
b = t.length > 500 ? t.slice(-500) : t;
|
|
487
1035
|
let n = x;
|
|
488
1036
|
x = [];
|
|
489
|
-
for (let e of n) e(
|
|
1037
|
+
for (let e of n) e(b);
|
|
490
1038
|
}), s.hot.on("svelte-devtools:fps", (e) => {
|
|
491
|
-
|
|
1039
|
+
T.push(e), T.length > 1200 && (T = T.slice(-1200)), D().recordFpsSample(e);
|
|
492
1040
|
}), s.hot.on("svelte-devtools:runtime-error", (e) => {
|
|
493
|
-
|
|
1041
|
+
w.push(e), w.length > 200 && (w = w.slice(-200));
|
|
494
1042
|
}), s.hot.on("svelte-devtools:reactive-graph", (e) => {
|
|
495
1043
|
let t = Array.isArray(e?.nodes) ? e.nodes : [], n = Array.isArray(e?.edges) ? e.edges : [];
|
|
496
|
-
|
|
497
|
-
nodes: t.length >
|
|
498
|
-
edges: n.length >
|
|
1044
|
+
v = {
|
|
1045
|
+
nodes: t.length > r ? t.slice(0, r) : t,
|
|
1046
|
+
edges: n.length > i ? n.slice(0, i) : n
|
|
499
1047
|
};
|
|
500
|
-
let
|
|
501
|
-
|
|
502
|
-
for (let e of
|
|
1048
|
+
let a = y;
|
|
1049
|
+
y = [];
|
|
1050
|
+
for (let e of a) e(v);
|
|
503
1051
|
});
|
|
504
|
-
let
|
|
1052
|
+
let a = () => {
|
|
505
1053
|
let e = /* @__PURE__ */ new Set(), t = s?.config?.server ?? o?.server, n = t?.port ?? 5173, r = t?.https ? "https" : "http";
|
|
506
1054
|
for (let t of [
|
|
507
1055
|
"localhost",
|
|
@@ -509,8 +1057,8 @@ function z(n = {}) {
|
|
|
509
1057
|
"[::1]"
|
|
510
1058
|
]) e.add(`${r}://${t}`), e.add(`${r}://${t}:${n}`);
|
|
511
1059
|
return e;
|
|
512
|
-
},
|
|
513
|
-
let t =
|
|
1060
|
+
}, f = (e) => {
|
|
1061
|
+
let t = a(), n = e.headers.origin, r = e.headers.referer, i = (() => {
|
|
514
1062
|
if (typeof n == "string" && n) return n;
|
|
515
1063
|
if (typeof r == "string" && r) try {
|
|
516
1064
|
return new URL(r).origin;
|
|
@@ -520,15 +1068,15 @@ function z(n = {}) {
|
|
|
520
1068
|
return null;
|
|
521
1069
|
})();
|
|
522
1070
|
if (i !== null && !t.has(i)) return !1;
|
|
523
|
-
let
|
|
524
|
-
return (Array.isArray(
|
|
525
|
-
},
|
|
1071
|
+
let o = e.headers["x-svelte-devtools-token"];
|
|
1072
|
+
return (Array.isArray(o) ? o[0] : o) === u;
|
|
1073
|
+
}, _ = e.resolve(Q, "client");
|
|
526
1074
|
s.middlewares.use("/.svelte-devtools", (n, r, i) => {
|
|
527
|
-
let a = (n.url || "/").split("?")[0], o = a === "/" || a === "" || a === "/index.html", s = o ? e.join(
|
|
1075
|
+
let a = (n.url || "/").split("?")[0], o = a === "/" || a === "" || a === "/index.html", s = o ? e.join(_, "index.html") : e.join(_, a);
|
|
528
1076
|
try {
|
|
529
1077
|
if (t.statSync(s).isFile()) {
|
|
530
1078
|
let n = e.extname(s).toLowerCase();
|
|
531
|
-
if (r.setHeader("Content-Type",
|
|
1079
|
+
if (r.setHeader("Content-Type", g[n] || "application/octet-stream"), o) {
|
|
532
1080
|
let e = t.readFileSync(s, "utf-8"), n = `<meta name="svelte-devtools-token" content="${u}">`, i = e.includes("</head>") ? e.replace("</head>", ` ${n}\n</head>`) : `${n}\n${e}`;
|
|
533
1081
|
r.end(i);
|
|
534
1082
|
} else t.createReadStream(s).pipe(r);
|
|
@@ -541,7 +1089,7 @@ function z(n = {}) {
|
|
|
541
1089
|
t.statusCode = 405, t.end("Method not allowed");
|
|
542
1090
|
return;
|
|
543
1091
|
}
|
|
544
|
-
if (!
|
|
1092
|
+
if (!f(e)) {
|
|
545
1093
|
t.statusCode = 403, t.end("Forbidden");
|
|
546
1094
|
return;
|
|
547
1095
|
}
|
|
@@ -555,7 +1103,7 @@ function z(n = {}) {
|
|
|
555
1103
|
a || (i += e.toString(), i.length > 1e6 && (a = !0, t.statusCode = 413, t.end("Payload Too Large")));
|
|
556
1104
|
}), e.on("end", async () => {
|
|
557
1105
|
if (!a) try {
|
|
558
|
-
let { method: e, args: n } = JSON.parse(i), r =
|
|
1106
|
+
let { method: e, args: n } = JSON.parse(i), r = A()[e];
|
|
559
1107
|
if (!r) throw Error(`Unknown RPC method: ${e}`);
|
|
560
1108
|
let a = await r(...n);
|
|
561
1109
|
t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify(a));
|
|
@@ -564,7 +1112,7 @@ function z(n = {}) {
|
|
|
564
1112
|
}
|
|
565
1113
|
});
|
|
566
1114
|
}), s.middlewares.use("/__svelte-devtools/asset", (n, r) => {
|
|
567
|
-
if (!
|
|
1115
|
+
if (!f(n)) {
|
|
568
1116
|
r.statusCode = 403, r.end("Forbidden");
|
|
569
1117
|
return;
|
|
570
1118
|
}
|
|
@@ -573,48 +1121,87 @@ function z(n = {}) {
|
|
|
573
1121
|
r.statusCode = 400, r.end("Missing path parameter");
|
|
574
1122
|
return;
|
|
575
1123
|
}
|
|
576
|
-
let
|
|
1124
|
+
let l = C(c), u = t.realpathSync(l.staticDir), d = e.resolve(s), p;
|
|
577
1125
|
try {
|
|
578
|
-
|
|
1126
|
+
p = t.realpathSync(d);
|
|
579
1127
|
} catch {
|
|
580
1128
|
r.statusCode = 404, r.end("Not found");
|
|
581
1129
|
return;
|
|
582
1130
|
}
|
|
583
|
-
if (!
|
|
1131
|
+
if (!p.startsWith(u + e.sep) && p !== u) {
|
|
584
1132
|
r.statusCode = 403, r.end("Forbidden");
|
|
585
1133
|
return;
|
|
586
1134
|
}
|
|
587
1135
|
try {
|
|
588
|
-
if (!t.statSync(
|
|
589
|
-
let n = e.extname(
|
|
590
|
-
r.setHeader("Content-Type",
|
|
1136
|
+
if (!t.statSync(p).isFile()) throw Error("Not a file");
|
|
1137
|
+
let n = e.extname(p).toLowerCase();
|
|
1138
|
+
r.setHeader("Content-Type", g[n] || "application/octet-stream"), t.createReadStream(p).pipe(r);
|
|
591
1139
|
} catch {
|
|
592
1140
|
r.statusCode = 404, r.end("Not found");
|
|
593
1141
|
}
|
|
594
1142
|
});
|
|
1143
|
+
let S = () => oe({
|
|
1144
|
+
getProject: () => C(c),
|
|
1145
|
+
getRoutes: () => d(C(c).routesDir),
|
|
1146
|
+
getLiveComponents: () => p,
|
|
1147
|
+
getComponentRelations: () => O(c),
|
|
1148
|
+
getRenderProfiles: () => m,
|
|
1149
|
+
getReactiveGraph: () => A()["svelte-devtools:get-reactive-graph"](),
|
|
1150
|
+
getLoadProfiles: () => h,
|
|
1151
|
+
getFpsSamples: () => T,
|
|
1152
|
+
sessions: D()
|
|
1153
|
+
});
|
|
1154
|
+
n.httpServer?.once("listening", () => {
|
|
1155
|
+
try {
|
|
1156
|
+
let e = n.httpServer?.address();
|
|
1157
|
+
if (!e || typeof e == "string") return;
|
|
1158
|
+
let t = `claude mcp add --transport http svelte ${`${n.config.server?.https ? "https" : "http"}://${e.address === "::" || e.address === "0.0.0.0" || e.address === "::1" || e.address === "127.0.0.1" ? "localhost" : e.family === "IPv6" ? `[${e.address}]` : e.address}:${e.port}/__svelte-devtools/mcp`} --header x-svelte-devtools-token:${u}`;
|
|
1159
|
+
n.config.logger.info(""), n.config.logger.info(" svelte-devtools MCP ready — register with Claude Code:"), n.config.logger.info(` ${t}`), n.config.logger.info("");
|
|
1160
|
+
} catch {}
|
|
1161
|
+
}), s.middlewares.use("/__svelte-devtools/mcp", async (e, t) => {
|
|
1162
|
+
let r = e.headers["x-svelte-devtools-token"];
|
|
1163
|
+
if ((Array.isArray(r) ? r[0] : r) !== u) {
|
|
1164
|
+
t.statusCode = 403, t.end("Forbidden");
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
let i = S(), a = new l({
|
|
1168
|
+
sessionIdGenerator: void 0,
|
|
1169
|
+
enableJsonResponse: !0
|
|
1170
|
+
});
|
|
1171
|
+
a.onerror = (e) => {
|
|
1172
|
+
n.config.logger.error(`[svelte-devtools] MCP transport error: ${e.stack || e.message}`);
|
|
1173
|
+
};
|
|
1174
|
+
try {
|
|
1175
|
+
await i.connect(a), await a.handleRequest(e, t);
|
|
1176
|
+
} catch (e) {
|
|
1177
|
+
n.config.logger.error(`[svelte-devtools] MCP handler error: ${e instanceof Error ? e.stack || e.message : String(e)}`), t.headersSent || (t.statusCode = 500, t.setHeader("Content-Type", "application/json"), t.end(JSON.stringify({ error: String(e) })));
|
|
1178
|
+
} finally {
|
|
1179
|
+
await a.close().catch(() => {}), await i.close().catch(() => {});
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
595
1182
|
},
|
|
596
1183
|
resolveId(e, t) {
|
|
597
1184
|
if (o?.command === "serve") {
|
|
598
|
-
if (e === "virtual:svelte-devtools-runtime") return
|
|
599
|
-
if (i && e === "svelte/internal/client" && t && !t.includes("node_modules") && !t.startsWith("\0")) return
|
|
1185
|
+
if (e === "virtual:svelte-devtools-runtime") return ee;
|
|
1186
|
+
if (i && e === "svelte/internal/client" && t && !t.includes("node_modules") && !t.startsWith("\0")) return F;
|
|
600
1187
|
}
|
|
601
1188
|
},
|
|
602
1189
|
load(e) {
|
|
603
1190
|
if (o?.command === "serve") {
|
|
604
|
-
if (e === "\0virtual:svelte-devtools-runtime") return
|
|
605
|
-
if (e === "\0svelte-devtools:wrapped-client") return
|
|
1191
|
+
if (e === "\0virtual:svelte-devtools-runtime") return L;
|
|
1192
|
+
if (e === "\0svelte-devtools:wrapped-client") return I;
|
|
606
1193
|
}
|
|
607
1194
|
},
|
|
608
1195
|
transformIndexHtml() {
|
|
609
1196
|
return o.command === "serve" ? [{
|
|
610
1197
|
tag: "script",
|
|
611
1198
|
attrs: { type: "module" },
|
|
612
|
-
children: `import '${
|
|
1199
|
+
children: `import '${P}'`,
|
|
613
1200
|
injectTo: "head-prepend"
|
|
614
1201
|
}] : [];
|
|
615
1202
|
},
|
|
616
1203
|
devtools: { setup(t) {
|
|
617
|
-
let n = e.resolve(
|
|
1204
|
+
let n = e.resolve(Q, "client");
|
|
618
1205
|
t.views.hostStatic("/.svelte-devtools/", n), t.docks.register({
|
|
619
1206
|
id: "svelte-devtools",
|
|
620
1207
|
title: "Svelte",
|
|
@@ -623,14 +1210,14 @@ function z(n = {}) {
|
|
|
623
1210
|
url: "/.svelte-devtools/",
|
|
624
1211
|
category: "framework"
|
|
625
1212
|
});
|
|
626
|
-
let r =
|
|
1213
|
+
let r = A();
|
|
627
1214
|
for (let [e, n] of Object.entries(r)) t.rpc.register({
|
|
628
1215
|
name: e,
|
|
629
1216
|
handler: n
|
|
630
1217
|
});
|
|
631
1218
|
} }
|
|
632
1219
|
};
|
|
633
|
-
function
|
|
1220
|
+
function M() {
|
|
634
1221
|
if (!s) return {
|
|
635
1222
|
modules: [],
|
|
636
1223
|
cycles: []
|
|
@@ -648,16 +1235,16 @@ function z(n = {}) {
|
|
|
648
1235
|
} catch {}
|
|
649
1236
|
for (let o of a) {
|
|
650
1237
|
if (!o.file || o.file.includes("node_modules")) continue;
|
|
651
|
-
let a = e.relative(
|
|
1238
|
+
let a = e.relative(c, o.file);
|
|
652
1239
|
if (a.startsWith("..") || r.has(o.file)) continue;
|
|
653
|
-
let s = e.extname(o.file).toLowerCase(),
|
|
1240
|
+
let s = e.extname(o.file).toLowerCase(), l = s === ".svelte" ? "svelte" : s === ".ts" || s === ".js" ? s === ".ts" ? "ts" : "js" : s === ".css" ? "css" : "other", u;
|
|
654
1241
|
try {
|
|
655
1242
|
u = t.statSync(o.file).size;
|
|
656
1243
|
} catch {}
|
|
657
1244
|
let d = {
|
|
658
1245
|
id: a,
|
|
659
1246
|
file: o.file,
|
|
660
|
-
type:
|
|
1247
|
+
type: l,
|
|
661
1248
|
importedBy: [],
|
|
662
1249
|
imports: [],
|
|
663
1250
|
size: u
|
|
@@ -672,7 +1259,7 @@ function z(n = {}) {
|
|
|
672
1259
|
n.has(a) || (n.add(a), t.imports.push(a)), e.importedBy.includes(t.id) || e.importedBy.push(t.id);
|
|
673
1260
|
}
|
|
674
1261
|
}
|
|
675
|
-
let o = [],
|
|
1262
|
+
let o = [], l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set(), d = [];
|
|
676
1263
|
function f(e) {
|
|
677
1264
|
if (u.has(e)) {
|
|
678
1265
|
let t = d.indexOf(e);
|
|
@@ -682,8 +1269,8 @@ function z(n = {}) {
|
|
|
682
1269
|
}
|
|
683
1270
|
return;
|
|
684
1271
|
}
|
|
685
|
-
if (
|
|
686
|
-
|
|
1272
|
+
if (l.has(e)) return;
|
|
1273
|
+
l.add(e), u.add(e), d.push(e);
|
|
687
1274
|
let t = i.get(e);
|
|
688
1275
|
if (t) for (let e of t.imports) f(e);
|
|
689
1276
|
d.pop(), u.delete(e);
|
|
@@ -696,7 +1283,7 @@ function z(n = {}) {
|
|
|
696
1283
|
cycles: o
|
|
697
1284
|
};
|
|
698
1285
|
}
|
|
699
|
-
async function
|
|
1286
|
+
async function N(e) {
|
|
700
1287
|
let t = {
|
|
701
1288
|
url: e,
|
|
702
1289
|
title: "",
|
|
@@ -706,7 +1293,7 @@ function z(n = {}) {
|
|
|
706
1293
|
issues: []
|
|
707
1294
|
};
|
|
708
1295
|
try {
|
|
709
|
-
|
|
1296
|
+
$(e);
|
|
710
1297
|
let n = await (await fetch(e)).text(), r = /<meta\s+([^>]*?)\/?>/gi, i = /(?:property|name)=["']([^"']+)["']/, a = /content=["']([^"']+)["']/, o;
|
|
711
1298
|
for (; (o = r.exec(n)) !== null;) {
|
|
712
1299
|
let e = o[1], n = e.match(i), r = e.match(a);
|
|
@@ -733,11 +1320,11 @@ function z(n = {}) {
|
|
|
733
1320
|
return t;
|
|
734
1321
|
}
|
|
735
1322
|
function R() {
|
|
736
|
-
let n = e.resolve(
|
|
1323
|
+
let n = e.resolve(c, ".svelte-kit/output"), r = e.resolve(c, "build/client"), i = [];
|
|
737
1324
|
for (let a of [
|
|
738
1325
|
n,
|
|
739
1326
|
r,
|
|
740
|
-
e.resolve(
|
|
1327
|
+
e.resolve(c, "build")
|
|
741
1328
|
]) try {
|
|
742
1329
|
let n = (r) => {
|
|
743
1330
|
for (let a of t.readdirSync(r, { withFileTypes: !0 })) {
|
|
@@ -755,7 +1342,7 @@ function z(n = {}) {
|
|
|
755
1342
|
let n = t.statSync(o);
|
|
756
1343
|
i.push({
|
|
757
1344
|
name: a.name,
|
|
758
|
-
file: e.relative(
|
|
1345
|
+
file: e.relative(c, o),
|
|
759
1346
|
size: n.size,
|
|
760
1347
|
modules: [],
|
|
761
1348
|
isEntry: a.name.includes("index") || a.name.includes("start")
|
|
@@ -772,7 +1359,7 @@ function z(n = {}) {
|
|
|
772
1359
|
};
|
|
773
1360
|
}
|
|
774
1361
|
return [
|
|
775
|
-
|
|
1362
|
+
j,
|
|
776
1363
|
{
|
|
777
1364
|
name: "vite-devtools-svelte:tracking",
|
|
778
1365
|
enforce: "post",
|
|
@@ -780,7 +1367,7 @@ function z(n = {}) {
|
|
|
780
1367
|
if (!i || !t.endsWith(".svelte") || t.includes("node_modules") || o?.command !== "serve" || !e.includes("$.push(")) return null;
|
|
781
1368
|
let n = JSON.stringify(t);
|
|
782
1369
|
return {
|
|
783
|
-
code: `import '${
|
|
1370
|
+
code: `import '${P}';\n` + e.replace(/(\$\.push\([^)]+\);?)/, `if (typeof window !== 'undefined' && window.__SVELTE_DEVTOOLS__) { window.__SVELTE_DEVTOOLS__._pendingFile = ${n}; }\n$1`),
|
|
784
1371
|
map: null
|
|
785
1372
|
};
|
|
786
1373
|
}
|
|
@@ -816,14 +1403,15 @@ export const load = async (event) => {
|
|
|
816
1403
|
name: "vite-devtools-svelte:load-profile-server",
|
|
817
1404
|
configureServer() {
|
|
818
1405
|
globalThis.__svelte_devtools_record_load = (e, t, n, r, i) => {
|
|
819
|
-
|
|
1406
|
+
let a = {
|
|
820
1407
|
route: e,
|
|
821
1408
|
file: t,
|
|
822
1409
|
type: n,
|
|
823
1410
|
duration: Math.round(r * 100) / 100,
|
|
824
1411
|
dataSize: i,
|
|
825
1412
|
timestamp: Date.now()
|
|
826
|
-
}
|
|
1413
|
+
};
|
|
1414
|
+
h.push(a), h.length > 200 && (h = h.slice(-200)), D().recordLoadProfile(a);
|
|
827
1415
|
};
|
|
828
1416
|
}
|
|
829
1417
|
},
|
|
@@ -846,10 +1434,11 @@ export const load = async (event) => {
|
|
|
846
1434
|
t(e, n);
|
|
847
1435
|
};
|
|
848
1436
|
}
|
|
849
|
-
}
|
|
1437
|
+
},
|
|
1438
|
+
de()
|
|
850
1439
|
];
|
|
851
1440
|
}
|
|
852
1441
|
//#endregion
|
|
853
|
-
export {
|
|
1442
|
+
export { pe as svelteDevtools };
|
|
854
1443
|
|
|
855
1444
|
//# sourceMappingURL=index.mjs.map
|