@xiaou66/vite-plugin-vue-mcp-next 0.0.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/LICENSE +21 -0
- package/README.md +531 -0
- package/dist/index.cjs +1519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +410 -0
- package/dist/index.d.ts +410 -0
- package/dist/index.js +1482 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/client.cjs +691 -0
- package/dist/runtime/client.cjs.map +1 -0
- package/dist/runtime/client.d.cts +30 -0
- package/dist/runtime/client.d.ts +30 -0
- package/dist/runtime/client.js +672 -0
- package/dist/runtime/client.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
// src/runtime/client.ts
|
|
2
|
+
import { createHotContext } from "vite-hot-client";
|
|
3
|
+
|
|
4
|
+
// src/runtime/consoleHook.ts
|
|
5
|
+
import { nanoid } from "nanoid";
|
|
6
|
+
|
|
7
|
+
// src/shared/serialization.ts
|
|
8
|
+
function safeStringify(value) {
|
|
9
|
+
if (typeof value === "string") {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
13
|
+
const serialized = JSON.stringify(
|
|
14
|
+
value,
|
|
15
|
+
(_key, current) => {
|
|
16
|
+
if (typeof current !== "object" || current === null) {
|
|
17
|
+
return current;
|
|
18
|
+
}
|
|
19
|
+
if (seen.has(current)) {
|
|
20
|
+
return "[Circular]";
|
|
21
|
+
}
|
|
22
|
+
seen.add(current);
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
return serialized;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/runtime/consoleHook.ts
|
|
30
|
+
function installConsoleHook(options) {
|
|
31
|
+
const originalConsole = {
|
|
32
|
+
log: console.log,
|
|
33
|
+
info: console.info,
|
|
34
|
+
warn: console.warn,
|
|
35
|
+
error: console.error,
|
|
36
|
+
debug: console.debug
|
|
37
|
+
};
|
|
38
|
+
const emit = (level, args) => {
|
|
39
|
+
options.send({
|
|
40
|
+
id: nanoid(),
|
|
41
|
+
pageId: options.pageId,
|
|
42
|
+
source: "hook",
|
|
43
|
+
level,
|
|
44
|
+
message: args.map((arg) => safeStringify(arg)).join(" "),
|
|
45
|
+
args,
|
|
46
|
+
timestamp: Date.now()
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
["log", "info", "warn", "error", "debug"].forEach((level) => {
|
|
50
|
+
console[level] = (...args) => {
|
|
51
|
+
emit(level, args);
|
|
52
|
+
originalConsole[level](...args);
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
const onError = (event) => {
|
|
56
|
+
options.send({
|
|
57
|
+
id: nanoid(),
|
|
58
|
+
pageId: options.pageId,
|
|
59
|
+
source: "hook",
|
|
60
|
+
level: "error",
|
|
61
|
+
message: event.message,
|
|
62
|
+
stack: event.error instanceof Error ? event.error.stack : void 0,
|
|
63
|
+
timestamp: Date.now()
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
window.addEventListener("error", onError);
|
|
67
|
+
return () => {
|
|
68
|
+
Object.assign(console, originalConsole);
|
|
69
|
+
window.removeEventListener("error", onError);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/runtime/networkHook.ts
|
|
74
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
75
|
+
|
|
76
|
+
// src/shared/sanitize.ts
|
|
77
|
+
function truncateText(text, maxLength) {
|
|
78
|
+
if (text.length <= maxLength) {
|
|
79
|
+
return { text, truncated: false, originalLength: text.length };
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
text: text.slice(0, maxLength),
|
|
83
|
+
truncated: true,
|
|
84
|
+
originalLength: text.length
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function maskHeaders(headers = {}, maskNames = []) {
|
|
88
|
+
const normalizedMaskNames = new Set(
|
|
89
|
+
maskNames.map((name) => name.toLowerCase())
|
|
90
|
+
);
|
|
91
|
+
return Object.fromEntries(
|
|
92
|
+
Object.entries(headers).map(([name, value]) => [
|
|
93
|
+
name,
|
|
94
|
+
normalizedMaskNames.has(name.toLowerCase()) ? "[masked]" : value
|
|
95
|
+
])
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/shared/url.ts
|
|
100
|
+
function parseRequestQuery(url) {
|
|
101
|
+
const parsed = new URL(url, "http://vite-plugin-vue-mcp-next.local");
|
|
102
|
+
const queryEntries = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const [key, value] of parsed.searchParams.entries()) {
|
|
104
|
+
const existing = queryEntries.get(key);
|
|
105
|
+
if (existing === void 0) {
|
|
106
|
+
queryEntries.set(key, value);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(existing)) {
|
|
110
|
+
existing.push(value);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
queryEntries.set(key, [existing, value]);
|
|
114
|
+
}
|
|
115
|
+
return Object.fromEntries(queryEntries);
|
|
116
|
+
}
|
|
117
|
+
function safeUrlPathname(url) {
|
|
118
|
+
try {
|
|
119
|
+
return new URL(url).pathname;
|
|
120
|
+
} catch {
|
|
121
|
+
return url.split("?")[0] || "/";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/runtime/networkHook.ts
|
|
126
|
+
function createHookNetworkRecord(input) {
|
|
127
|
+
return {
|
|
128
|
+
id: nanoid2(),
|
|
129
|
+
pageId: input.pageId,
|
|
130
|
+
source: "hook",
|
|
131
|
+
url: input.url,
|
|
132
|
+
method: input.method,
|
|
133
|
+
requestHeaders: maskHeaders(input.requestHeaders, input.maskHeaders),
|
|
134
|
+
requestQuery: parseRequestQuery(input.url),
|
|
135
|
+
requestBody: input.requestBody,
|
|
136
|
+
startedAt: input.startedAt
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function installNetworkHook(options) {
|
|
140
|
+
const originalFetch = window.fetch.bind(window);
|
|
141
|
+
const XMLHttpRequestCtor = window.XMLHttpRequest;
|
|
142
|
+
const originalOpen = XMLHttpRequestCtor?.prototype.open;
|
|
143
|
+
const originalSend = XMLHttpRequestCtor?.prototype.send;
|
|
144
|
+
window.fetch = createFetchHook(originalFetch, options);
|
|
145
|
+
if (XMLHttpRequestCtor && originalOpen && originalSend) {
|
|
146
|
+
installXhrHook(XMLHttpRequestCtor, originalOpen, originalSend, options);
|
|
147
|
+
}
|
|
148
|
+
return () => {
|
|
149
|
+
window.fetch = originalFetch;
|
|
150
|
+
if (XMLHttpRequestCtor && originalOpen && originalSend) {
|
|
151
|
+
XMLHttpRequestCtor.prototype.open = originalOpen;
|
|
152
|
+
XMLHttpRequestCtor.prototype.send = originalSend;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function createFetchHook(originalFetch, options) {
|
|
157
|
+
return async (input, init) => {
|
|
158
|
+
const startedAt = Date.now();
|
|
159
|
+
const record = createHookNetworkRecord({
|
|
160
|
+
pageId: options.pageId,
|
|
161
|
+
url: getFetchUrl(input),
|
|
162
|
+
method: getFetchMethod(input, init),
|
|
163
|
+
requestHeaders: headersToRecord(
|
|
164
|
+
init?.headers ?? (input instanceof Request ? input.headers : void 0)
|
|
165
|
+
),
|
|
166
|
+
requestBody: init?.body,
|
|
167
|
+
maskHeaders: options.maskHeaders,
|
|
168
|
+
startedAt
|
|
169
|
+
});
|
|
170
|
+
try {
|
|
171
|
+
const response = await originalFetch(input, init);
|
|
172
|
+
const endedAt = Date.now();
|
|
173
|
+
options.send({
|
|
174
|
+
...record,
|
|
175
|
+
status: response.status,
|
|
176
|
+
responseHeaders: headersToRecord(response.headers),
|
|
177
|
+
responseBody: await readResponseBody(response, options.maxBodySize),
|
|
178
|
+
endedAt,
|
|
179
|
+
durationMs: endedAt - startedAt
|
|
180
|
+
});
|
|
181
|
+
return response;
|
|
182
|
+
} catch (error) {
|
|
183
|
+
const endedAt = Date.now();
|
|
184
|
+
options.send({
|
|
185
|
+
...record,
|
|
186
|
+
error: error instanceof Error ? error.message : String(error),
|
|
187
|
+
endedAt,
|
|
188
|
+
durationMs: endedAt - startedAt
|
|
189
|
+
});
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function installXhrHook(XMLHttpRequestCtor, originalOpen, originalSend, options) {
|
|
195
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
196
|
+
XMLHttpRequestCtor.prototype.open = function open(method, url, async, username, password) {
|
|
197
|
+
const args = [method, url, async, username, password].filter(
|
|
198
|
+
(item) => item !== void 0
|
|
199
|
+
);
|
|
200
|
+
states.set(this, { method, url: String(url), startedAt: 0 });
|
|
201
|
+
Reflect.apply(originalOpen, this, args);
|
|
202
|
+
};
|
|
203
|
+
XMLHttpRequestCtor.prototype.send = function send(...args) {
|
|
204
|
+
const [body] = args;
|
|
205
|
+
const state = states.get(this);
|
|
206
|
+
if (state) {
|
|
207
|
+
state.startedAt = Date.now();
|
|
208
|
+
state.body = body;
|
|
209
|
+
const record = createHookNetworkRecord({
|
|
210
|
+
pageId: options.pageId,
|
|
211
|
+
url: state.url,
|
|
212
|
+
method: state.method,
|
|
213
|
+
requestBody: body,
|
|
214
|
+
maskHeaders: options.maskHeaders,
|
|
215
|
+
startedAt: state.startedAt
|
|
216
|
+
});
|
|
217
|
+
this.addEventListener("loadend", () => {
|
|
218
|
+
const endedAt = Date.now();
|
|
219
|
+
options.send({
|
|
220
|
+
...record,
|
|
221
|
+
status: this.status,
|
|
222
|
+
responseHeaders: parseRawHeaders(this.getAllResponseHeaders()),
|
|
223
|
+
responseBody: truncateText(
|
|
224
|
+
safeReadXhrResponseText(this),
|
|
225
|
+
options.maxBodySize
|
|
226
|
+
).text,
|
|
227
|
+
endedAt,
|
|
228
|
+
durationMs: endedAt - state.startedAt
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
Reflect.apply(originalSend, this, args);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function getFetchUrl(input) {
|
|
236
|
+
if (input instanceof Request) {
|
|
237
|
+
return input.url;
|
|
238
|
+
}
|
|
239
|
+
return String(input);
|
|
240
|
+
}
|
|
241
|
+
function getFetchMethod(input, init) {
|
|
242
|
+
if (init?.method) {
|
|
243
|
+
return init.method.toUpperCase();
|
|
244
|
+
}
|
|
245
|
+
if (input instanceof Request) {
|
|
246
|
+
return input.method.toUpperCase();
|
|
247
|
+
}
|
|
248
|
+
return "GET";
|
|
249
|
+
}
|
|
250
|
+
function headersToRecord(headers) {
|
|
251
|
+
if (!headers) {
|
|
252
|
+
return {};
|
|
253
|
+
}
|
|
254
|
+
return Object.fromEntries(new Headers(headers).entries());
|
|
255
|
+
}
|
|
256
|
+
async function readResponseBody(response, maxBodySize) {
|
|
257
|
+
try {
|
|
258
|
+
return truncateText(await response.clone().text(), maxBodySize).text;
|
|
259
|
+
} catch {
|
|
260
|
+
return void 0;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function parseRawHeaders(rawHeaders) {
|
|
264
|
+
return Object.fromEntries(
|
|
265
|
+
rawHeaders.trim().split(/\r?\n/).filter(Boolean).map((line) => {
|
|
266
|
+
const index = line.indexOf(":");
|
|
267
|
+
return [
|
|
268
|
+
line.slice(0, index).trim().toLowerCase(),
|
|
269
|
+
line.slice(index + 1).trim()
|
|
270
|
+
];
|
|
271
|
+
})
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
function safeReadXhrResponseText(xhr) {
|
|
275
|
+
try {
|
|
276
|
+
return xhr.responseText;
|
|
277
|
+
} catch {
|
|
278
|
+
return "";
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/runtime/pageIdentity.ts
|
|
283
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
284
|
+
function createRuntimePageId() {
|
|
285
|
+
return `runtime-${nanoid3()}`;
|
|
286
|
+
}
|
|
287
|
+
function getRuntimePageIdentity(input) {
|
|
288
|
+
return {
|
|
289
|
+
pageId: createRuntimePageId(),
|
|
290
|
+
source: "runtime",
|
|
291
|
+
url: input.href,
|
|
292
|
+
pathname: safeUrlPathname(input.href),
|
|
293
|
+
title: input.title,
|
|
294
|
+
connected: true,
|
|
295
|
+
readyState: input.readyState,
|
|
296
|
+
viewport: {
|
|
297
|
+
width: input.innerWidth,
|
|
298
|
+
height: input.innerHeight
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/runtime/vueBridge.ts
|
|
304
|
+
import {
|
|
305
|
+
devtools,
|
|
306
|
+
devtoolsRouterInfo,
|
|
307
|
+
devtoolsState,
|
|
308
|
+
getInspector,
|
|
309
|
+
stringify,
|
|
310
|
+
toggleHighPerfMode
|
|
311
|
+
} from "@vue/devtools-kit";
|
|
312
|
+
import { createRPCClient } from "vite-dev-rpc";
|
|
313
|
+
|
|
314
|
+
// src/runtime/domSnapshot.ts
|
|
315
|
+
function createDomSnapshot(root, options) {
|
|
316
|
+
let count = 0;
|
|
317
|
+
function visit(node, depth) {
|
|
318
|
+
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
322
|
+
return createTextSnapshot(node, options, () => {
|
|
323
|
+
count += 1;
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (!(node instanceof Element)) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const tag = node.tagName.toLowerCase();
|
|
330
|
+
if (["script", "style", "noscript"].includes(tag)) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
count += 1;
|
|
334
|
+
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
335
|
+
}
|
|
336
|
+
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
337
|
+
}
|
|
338
|
+
function queryDomElements(selector, limit) {
|
|
339
|
+
return Array.from(document.querySelectorAll(selector)).slice(0, limit).map((element) => ({
|
|
340
|
+
tag: element.tagName.toLowerCase(),
|
|
341
|
+
text: element.textContent.trim(),
|
|
342
|
+
attrs: collectAttrs(element),
|
|
343
|
+
rect: serializeRect(element.getBoundingClientRect())
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
function createTextSnapshot(node, options, markVisited) {
|
|
347
|
+
const text = node.textContent?.trim();
|
|
348
|
+
if (!text) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
markVisited();
|
|
352
|
+
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
353
|
+
}
|
|
354
|
+
function createElementSnapshot(node, tag, visitChild) {
|
|
355
|
+
const attrs = collectAttrs(node);
|
|
356
|
+
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
357
|
+
return {
|
|
358
|
+
tag,
|
|
359
|
+
...Object.keys(attrs).length ? { attrs } : {},
|
|
360
|
+
...children.length ? { children } : {}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function collectAttrs(element) {
|
|
364
|
+
const attrs = {};
|
|
365
|
+
for (const attr of Array.from(element.attributes)) {
|
|
366
|
+
attrs[attr.name] = attr.value;
|
|
367
|
+
}
|
|
368
|
+
if (element instanceof HTMLInputElement && element.type === "password") {
|
|
369
|
+
attrs.value = "[masked]";
|
|
370
|
+
}
|
|
371
|
+
return attrs;
|
|
372
|
+
}
|
|
373
|
+
function serializeRect(rect) {
|
|
374
|
+
return {
|
|
375
|
+
x: rect.x,
|
|
376
|
+
y: rect.y,
|
|
377
|
+
width: rect.width,
|
|
378
|
+
height: rect.height,
|
|
379
|
+
top: rect.top,
|
|
380
|
+
right: rect.right,
|
|
381
|
+
bottom: rect.bottom,
|
|
382
|
+
left: rect.left
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/runtime/evaluateExpression.ts
|
|
387
|
+
async function evaluateExpression(request) {
|
|
388
|
+
const value = runExpression(request.expression);
|
|
389
|
+
const result = request.awaitPromise === false ? value : await Promise.resolve(value);
|
|
390
|
+
return Promise.race([
|
|
391
|
+
Promise.resolve(result),
|
|
392
|
+
createTimeout(request.timeoutMs)
|
|
393
|
+
]);
|
|
394
|
+
}
|
|
395
|
+
function runExpression(expression) {
|
|
396
|
+
return new Function(`return (${expression})`)();
|
|
397
|
+
}
|
|
398
|
+
function createTimeout(timeoutMs) {
|
|
399
|
+
return new Promise((_, reject) => {
|
|
400
|
+
window.setTimeout(() => {
|
|
401
|
+
reject(
|
|
402
|
+
new Error(`evaluate_script timed out after ${String(timeoutMs)}ms`)
|
|
403
|
+
);
|
|
404
|
+
}, timeoutMs);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/runtime/devtoolsBridge.ts
|
|
409
|
+
function createRuntimeDevtoolsRpc(getRpc) {
|
|
410
|
+
return {
|
|
411
|
+
getDomTree(options) {
|
|
412
|
+
getRpc().onDomTreeUpdated(
|
|
413
|
+
options.event,
|
|
414
|
+
createDomSnapshot(document.documentElement, {
|
|
415
|
+
maxDepth: options.maxDepth,
|
|
416
|
+
maxNodes: options.maxNodes,
|
|
417
|
+
maxTextLength: options.maxTextLength
|
|
418
|
+
})
|
|
419
|
+
);
|
|
420
|
+
},
|
|
421
|
+
onDomTreeUpdated: () => void 0,
|
|
422
|
+
queryDom(options) {
|
|
423
|
+
getRpc().onDomQueryUpdated(
|
|
424
|
+
options.event,
|
|
425
|
+
queryDomElements(options.selector, options.limit)
|
|
426
|
+
);
|
|
427
|
+
},
|
|
428
|
+
onDomQueryUpdated: () => void 0,
|
|
429
|
+
async evaluateScript(options) {
|
|
430
|
+
try {
|
|
431
|
+
getRpc().onEvaluateScriptUpdated(options.event, {
|
|
432
|
+
ok: true,
|
|
433
|
+
value: await evaluateExpression(options)
|
|
434
|
+
});
|
|
435
|
+
} catch (error) {
|
|
436
|
+
getRpc().onEvaluateScriptUpdated(options.event, {
|
|
437
|
+
ok: false,
|
|
438
|
+
error: error instanceof Error ? error.message : String(error)
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
onEvaluateScriptUpdated: () => void 0
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/runtime/vueBridge.ts
|
|
447
|
+
var PINIA_INSPECTOR_ID = "pinia";
|
|
448
|
+
var COMPONENTS_INSPECTOR_ID = "components";
|
|
449
|
+
var COMPONENT_HIGHLIGHT_DURATION = 5e3;
|
|
450
|
+
var highlightComponentTimeout;
|
|
451
|
+
function installVueBridge(hot) {
|
|
452
|
+
devtools.init();
|
|
453
|
+
const rpcRef = {};
|
|
454
|
+
const rpc = createRPCClient(
|
|
455
|
+
"vite-plugin-vue-mcp-next",
|
|
456
|
+
hot,
|
|
457
|
+
createClientVueRuntimeRpc(() => {
|
|
458
|
+
if (!rpcRef.current) {
|
|
459
|
+
throw new Error("Vue runtime RPC is not initialized");
|
|
460
|
+
}
|
|
461
|
+
return rpcRef.current;
|
|
462
|
+
}),
|
|
463
|
+
{ timeout: -1 }
|
|
464
|
+
);
|
|
465
|
+
rpcRef.current = rpc;
|
|
466
|
+
}
|
|
467
|
+
function createClientVueRuntimeRpc(getRpc) {
|
|
468
|
+
return {
|
|
469
|
+
...createRuntimeDevtoolsRpc(getRpc),
|
|
470
|
+
async getInspectorTree(query) {
|
|
471
|
+
const inspectorTree = await devtools.api.getInspectorTree({
|
|
472
|
+
inspectorId: COMPONENTS_INSPECTOR_ID,
|
|
473
|
+
filter: query.componentName ?? ""
|
|
474
|
+
});
|
|
475
|
+
getRpc().onInspectorTreeUpdated(query.event, inspectorTree[0]);
|
|
476
|
+
},
|
|
477
|
+
onInspectorTreeUpdated: () => void 0,
|
|
478
|
+
async getInspectorState(query) {
|
|
479
|
+
const targetNode = await findComponentNode(query.componentName);
|
|
480
|
+
if (!targetNode) {
|
|
481
|
+
getRpc().onInspectorStateUpdated(
|
|
482
|
+
query.event,
|
|
483
|
+
createMissingComponentError(query.componentName)
|
|
484
|
+
);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const inspectorState = await devtools.api.getInspectorState({
|
|
488
|
+
inspectorId: COMPONENTS_INSPECTOR_ID,
|
|
489
|
+
nodeId: targetNode.id
|
|
490
|
+
});
|
|
491
|
+
getRpc().onInspectorStateUpdated(query.event, stringify(inspectorState));
|
|
492
|
+
},
|
|
493
|
+
onInspectorStateUpdated: () => void 0,
|
|
494
|
+
async editComponentState(query) {
|
|
495
|
+
const targetNode = await findComponentNode(query.componentName);
|
|
496
|
+
if (!targetNode) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
devtools.ctx.api.editInspectorState({
|
|
500
|
+
app: null,
|
|
501
|
+
inspectorId: COMPONENTS_INSPECTOR_ID,
|
|
502
|
+
nodeId: targetNode.id,
|
|
503
|
+
path: query.path,
|
|
504
|
+
state: {
|
|
505
|
+
remove: false,
|
|
506
|
+
value: parseStateValue(query.value, query.valueType)
|
|
507
|
+
},
|
|
508
|
+
type: query.valueType,
|
|
509
|
+
set: setStateValue
|
|
510
|
+
});
|
|
511
|
+
},
|
|
512
|
+
async highlightComponent(query) {
|
|
513
|
+
const targetNode = await findComponentNode(query.componentName);
|
|
514
|
+
if (!targetNode) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
if (highlightComponentTimeout) {
|
|
518
|
+
clearTimeout(highlightComponentTimeout);
|
|
519
|
+
}
|
|
520
|
+
callVueDevtoolsHook("componentHighlight", { uid: targetNode.id });
|
|
521
|
+
highlightComponentTimeout = setTimeout(() => {
|
|
522
|
+
callVueDevtoolsHook("componentUnhighlight");
|
|
523
|
+
}, COMPONENT_HIGHLIGHT_DURATION);
|
|
524
|
+
},
|
|
525
|
+
getRouterInfo(query) {
|
|
526
|
+
getRpc().onRouterInfoUpdated(
|
|
527
|
+
query.event,
|
|
528
|
+
JSON.stringify(devtoolsRouterInfo, null, 2)
|
|
529
|
+
);
|
|
530
|
+
},
|
|
531
|
+
onRouterInfoUpdated: () => void 0,
|
|
532
|
+
async getPiniaTree(query) {
|
|
533
|
+
const inspectorTree = await withPiniaHighPerfDisabled(
|
|
534
|
+
() => devtools.api.getInspectorTree({
|
|
535
|
+
inspectorId: PINIA_INSPECTOR_ID,
|
|
536
|
+
filter: ""
|
|
537
|
+
})
|
|
538
|
+
);
|
|
539
|
+
getRpc().onPiniaTreeUpdated(query.event, inspectorTree);
|
|
540
|
+
},
|
|
541
|
+
onPiniaTreeUpdated: () => void 0,
|
|
542
|
+
async getPiniaState(query) {
|
|
543
|
+
const result = await withPiniaHighPerfDisabled(async () => {
|
|
544
|
+
const payload = {
|
|
545
|
+
inspectorId: PINIA_INSPECTOR_ID,
|
|
546
|
+
nodeId: query.storeName
|
|
547
|
+
};
|
|
548
|
+
const inspector = getInspector(payload.inspectorId);
|
|
549
|
+
if (inspector) {
|
|
550
|
+
inspector.selectedNodeId = payload.nodeId;
|
|
551
|
+
}
|
|
552
|
+
return devtools.ctx.api.getInspectorState(payload);
|
|
553
|
+
});
|
|
554
|
+
getRpc().onPiniaInfoUpdated(query.event, stringify(result));
|
|
555
|
+
},
|
|
556
|
+
onPiniaInfoUpdated: () => void 0
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function setStateValue(object, path, value) {
|
|
560
|
+
if (!object || typeof object !== "object" || !path) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const keys = Array.isArray(path) ? path : [path];
|
|
564
|
+
const lastKey = keys.at(-1);
|
|
565
|
+
if (!lastKey) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
;
|
|
569
|
+
object[lastKey] = value;
|
|
570
|
+
}
|
|
571
|
+
function parseStateValue(value, valueType) {
|
|
572
|
+
if (valueType === "number") {
|
|
573
|
+
return Number(value);
|
|
574
|
+
}
|
|
575
|
+
if (valueType === "boolean") {
|
|
576
|
+
return value === "true";
|
|
577
|
+
}
|
|
578
|
+
if (valueType === "object" || valueType === "array") {
|
|
579
|
+
try {
|
|
580
|
+
return JSON.parse(value);
|
|
581
|
+
} catch {
|
|
582
|
+
return value;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return value;
|
|
586
|
+
}
|
|
587
|
+
function callVueDevtoolsHook(name, payload) {
|
|
588
|
+
const hooks = devtools.ctx.hooks;
|
|
589
|
+
hooks.callHook(name, payload);
|
|
590
|
+
}
|
|
591
|
+
async function findComponentNode(componentName) {
|
|
592
|
+
const inspectorTree = await devtools.api.getInspectorTree({
|
|
593
|
+
inspectorId: COMPONENTS_INSPECTOR_ID,
|
|
594
|
+
filter: ""
|
|
595
|
+
});
|
|
596
|
+
const nodes = flattenTree(inspectorTree[0]);
|
|
597
|
+
return nodes.find((node) => node.name === componentName);
|
|
598
|
+
}
|
|
599
|
+
function flattenTree(root) {
|
|
600
|
+
const result = [];
|
|
601
|
+
const traverse = (node) => {
|
|
602
|
+
if (!isInspectorNode(node)) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
result.push(node);
|
|
606
|
+
node.children?.forEach((child) => {
|
|
607
|
+
traverse(child);
|
|
608
|
+
});
|
|
609
|
+
};
|
|
610
|
+
traverse(root);
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
function isInspectorNode(node) {
|
|
614
|
+
return Boolean(
|
|
615
|
+
node && typeof node === "object" && typeof node.id === "string"
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
async function withPiniaHighPerfDisabled(callback) {
|
|
619
|
+
const highPerfModeEnabled = devtoolsState.highPerfModeEnabled;
|
|
620
|
+
if (highPerfModeEnabled) {
|
|
621
|
+
toggleHighPerfMode(false);
|
|
622
|
+
}
|
|
623
|
+
try {
|
|
624
|
+
return await callback();
|
|
625
|
+
} finally {
|
|
626
|
+
if (highPerfModeEnabled) {
|
|
627
|
+
toggleHighPerfMode(true);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function createMissingComponentError(componentName) {
|
|
632
|
+
return {
|
|
633
|
+
ok: false,
|
|
634
|
+
error: `component not found: ${componentName}`
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/runtime/client.ts
|
|
639
|
+
async function startRuntimeClient() {
|
|
640
|
+
const hot = await createHotContext("vite-plugin-vue-mcp-next", "/");
|
|
641
|
+
if (!hot) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
installVueBridge(hot);
|
|
645
|
+
const identity = getRuntimePageIdentity({
|
|
646
|
+
href: window.location.href,
|
|
647
|
+
title: document.title,
|
|
648
|
+
innerWidth: window.innerWidth,
|
|
649
|
+
innerHeight: window.innerHeight,
|
|
650
|
+
readyState: document.readyState
|
|
651
|
+
});
|
|
652
|
+
hot.send("vite-plugin-vue-mcp-next:page-connected", identity);
|
|
653
|
+
installConsoleHook({
|
|
654
|
+
pageId: identity.pageId,
|
|
655
|
+
send(record) {
|
|
656
|
+
hot.send("vite-plugin-vue-mcp-next:console-record", record);
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
installNetworkHook({
|
|
660
|
+
pageId: identity.pageId,
|
|
661
|
+
maxBodySize: 1e5,
|
|
662
|
+
maskHeaders: ["authorization", "cookie", "set-cookie"],
|
|
663
|
+
send(record) {
|
|
664
|
+
hot.send("vite-plugin-vue-mcp-next:network-record", record);
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
export {
|
|
669
|
+
evaluateExpression,
|
|
670
|
+
startRuntimeClient
|
|
671
|
+
};
|
|
672
|
+
//# sourceMappingURL=client.js.map
|