@xiaou66/vite-plugin-vue-mcp-next 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/dist/index.cjs +791 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -849
- package/dist/index.d.ts +5 -849
- package/dist/index.js +763 -54
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +934 -81
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.d.cts +5 -1
- package/dist/runtime/client.d.ts +5 -1
- package/dist/runtime/client.js +934 -81
- package/dist/runtime/client.js.map +1 -1
- package/dist/types-BKXdHkwk.d.cts +1019 -0
- package/dist/types-BKXdHkwk.d.ts +1019 -0
- package/package.json +3 -1
- package/skills/vite-mcp-next/SKILL.md +14 -6
package/dist/runtime/client.js
CHANGED
|
@@ -33,6 +33,7 @@ var RUNTIME_PAGE_CONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-connected";
|
|
|
33
33
|
var RUNTIME_PAGE_DISCONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-disconnected";
|
|
34
34
|
var RUNTIME_PAGE_HEARTBEAT_EVENT = "vite-plugin-vue-mcp-next:heartbeat";
|
|
35
35
|
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
36
|
+
var DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS = 2200;
|
|
36
37
|
var DEFAULT_OPTIONS = {
|
|
37
38
|
mcpPath: DEFAULT_MCP_PATH,
|
|
38
39
|
host: "localhost",
|
|
@@ -51,6 +52,16 @@ var DEFAULT_OPTIONS = {
|
|
|
51
52
|
skill: {
|
|
52
53
|
autoConfig: true
|
|
53
54
|
},
|
|
55
|
+
elementPicker: {
|
|
56
|
+
enabled: true,
|
|
57
|
+
shortcut: {
|
|
58
|
+
altKey: true,
|
|
59
|
+
shiftKey: true,
|
|
60
|
+
metaKey: false,
|
|
61
|
+
ctrlKey: false
|
|
62
|
+
},
|
|
63
|
+
toastDurationMs: DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS
|
|
64
|
+
},
|
|
54
65
|
runtime: {
|
|
55
66
|
mode: "auto",
|
|
56
67
|
evaluate: {
|
|
@@ -169,8 +180,227 @@ function installConsoleHook(options) {
|
|
|
169
180
|
};
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
// src/runtime/
|
|
183
|
+
// src/runtime/elementRegistry.ts
|
|
173
184
|
import { nanoid as nanoid2 } from "nanoid";
|
|
185
|
+
var RUNTIME_ELEMENT_ID_PREFIX = "runtime:vmcp_";
|
|
186
|
+
var RUNTIME_ELEMENT_ID_SIZE = 8;
|
|
187
|
+
var runtimeElementRegistry = createRuntimeElementRegistry();
|
|
188
|
+
function createRuntimeElementId() {
|
|
189
|
+
return `${RUNTIME_ELEMENT_ID_PREFIX}${nanoid2(RUNTIME_ELEMENT_ID_SIZE)}`;
|
|
190
|
+
}
|
|
191
|
+
function createRuntimeElementRegistry() {
|
|
192
|
+
const records = /* @__PURE__ */ new Map();
|
|
193
|
+
return {
|
|
194
|
+
register(element) {
|
|
195
|
+
const elementId = createRuntimeElementId();
|
|
196
|
+
records.set(elementId, {
|
|
197
|
+
elementId,
|
|
198
|
+
element,
|
|
199
|
+
createdAt: Date.now()
|
|
200
|
+
});
|
|
201
|
+
return elementId;
|
|
202
|
+
},
|
|
203
|
+
get(elementId) {
|
|
204
|
+
return records.get(elementId);
|
|
205
|
+
},
|
|
206
|
+
clear() {
|
|
207
|
+
records.clear();
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/runtime/elementPicker.ts
|
|
213
|
+
var MCP_ID_ATTR = "data-v-mcp-id";
|
|
214
|
+
var INTERNAL_ATTR = "data-v-mcp-internal";
|
|
215
|
+
var SUCCESS_MESSAGE = "\u5143\u7D20\u4F4D\u7F6E\u5DF2\u590D\u5236\uFF0C\u8BF7\u53D1\u9001\u7ED9 AI";
|
|
216
|
+
var COPY_FAILED_PREFIX = "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u5143\u7D20 ID";
|
|
217
|
+
var OVERLAY_Z_INDEX = "2147483647";
|
|
218
|
+
var TOAST_Z_INDEX = "2147483647";
|
|
219
|
+
function installElementPicker(options) {
|
|
220
|
+
if (!options.enabled) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const registry = runtimeElementRegistry;
|
|
224
|
+
const overlay = createOverlay();
|
|
225
|
+
let active = false;
|
|
226
|
+
let currentElement;
|
|
227
|
+
window.addEventListener("keydown", (event) => {
|
|
228
|
+
active = matchesShortcut(event, options.shortcut);
|
|
229
|
+
});
|
|
230
|
+
window.addEventListener("keyup", () => {
|
|
231
|
+
active = false;
|
|
232
|
+
currentElement = void 0;
|
|
233
|
+
updateOverlay(overlay);
|
|
234
|
+
});
|
|
235
|
+
window.addEventListener("mousemove", (event) => {
|
|
236
|
+
if (!active) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
currentElement = document.elementFromPoint(event.clientX, event.clientY) ?? void 0;
|
|
240
|
+
updateOverlay(overlay, currentElement);
|
|
241
|
+
});
|
|
242
|
+
window.addEventListener(
|
|
243
|
+
"click",
|
|
244
|
+
(event) => {
|
|
245
|
+
if (!active || !isElementLike(event.target)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
event.stopPropagation();
|
|
250
|
+
void copyAndNotify(event.target, registry, options.toastDurationMs);
|
|
251
|
+
},
|
|
252
|
+
true
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
function matchesShortcut(event, shortcut) {
|
|
256
|
+
return event.altKey === shortcut.altKey && event.shiftKey === shortcut.shiftKey && event.metaKey === shortcut.metaKey && event.ctrlKey === shortcut.ctrlKey;
|
|
257
|
+
}
|
|
258
|
+
function resolveElementId(element, registry) {
|
|
259
|
+
return element.getAttribute(MCP_ID_ATTR) ?? registry.register(element);
|
|
260
|
+
}
|
|
261
|
+
async function copyAndNotify(element, registry, toastDurationMs) {
|
|
262
|
+
const elementId = resolveElementId(element, registry);
|
|
263
|
+
const copied = await copyElementId(elementId);
|
|
264
|
+
showToast(
|
|
265
|
+
copied ? SUCCESS_MESSAGE : `${COPY_FAILED_PREFIX}: ${elementId}`,
|
|
266
|
+
toastDurationMs
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
async function copyElementId(elementId) {
|
|
270
|
+
try {
|
|
271
|
+
await navigator.clipboard.writeText(elementId);
|
|
272
|
+
return true;
|
|
273
|
+
} catch {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function createOverlay() {
|
|
278
|
+
const overlay = document.createElement("div");
|
|
279
|
+
markInternalElement(overlay);
|
|
280
|
+
Object.assign(overlay.style, {
|
|
281
|
+
position: "fixed",
|
|
282
|
+
pointerEvents: "none",
|
|
283
|
+
border: "2px solid #1d4ed8",
|
|
284
|
+
background: "rgba(29, 78, 216, 0.08)",
|
|
285
|
+
zIndex: OVERLAY_Z_INDEX,
|
|
286
|
+
display: "none"
|
|
287
|
+
});
|
|
288
|
+
document.body.appendChild(overlay);
|
|
289
|
+
return overlay;
|
|
290
|
+
}
|
|
291
|
+
function updateOverlay(overlay, element) {
|
|
292
|
+
if (!element || getElementAttr(element, INTERNAL_ATTR) === "true") {
|
|
293
|
+
overlay.style.display = "none";
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const rect = element.getBoundingClientRect();
|
|
297
|
+
Object.assign(overlay.style, {
|
|
298
|
+
display: "block",
|
|
299
|
+
left: `${String(rect.x)}px`,
|
|
300
|
+
top: `${String(rect.y)}px`,
|
|
301
|
+
width: `${String(rect.width)}px`,
|
|
302
|
+
height: `${String(rect.height)}px`
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function showToast(message, durationMs) {
|
|
306
|
+
const toast = document.createElement("div");
|
|
307
|
+
markInternalElement(toast);
|
|
308
|
+
toast.textContent = message;
|
|
309
|
+
Object.assign(toast.style, {
|
|
310
|
+
position: "fixed",
|
|
311
|
+
left: "50%",
|
|
312
|
+
bottom: "32px",
|
|
313
|
+
transform: "translateX(-50%)",
|
|
314
|
+
zIndex: TOAST_Z_INDEX,
|
|
315
|
+
padding: "8px 12px",
|
|
316
|
+
borderRadius: "6px",
|
|
317
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
318
|
+
color: "#fff",
|
|
319
|
+
fontSize: "13px",
|
|
320
|
+
pointerEvents: "none"
|
|
321
|
+
});
|
|
322
|
+
document.body.appendChild(toast);
|
|
323
|
+
globalThis.setTimeout(() => {
|
|
324
|
+
toast.remove();
|
|
325
|
+
}, durationMs);
|
|
326
|
+
}
|
|
327
|
+
function isElementLike(value) {
|
|
328
|
+
return Boolean(
|
|
329
|
+
value && typeof value === "object" && "getAttribute" in value && "getBoundingClientRect" in value
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
function markInternalElement(element) {
|
|
333
|
+
if (typeof element.setAttribute === "function") {
|
|
334
|
+
element.setAttribute(INTERNAL_ATTR, "true");
|
|
335
|
+
}
|
|
336
|
+
element.dataset.vMcpInternal = "true";
|
|
337
|
+
}
|
|
338
|
+
function getElementAttr(element, name) {
|
|
339
|
+
if (typeof element.getAttribute === "function") {
|
|
340
|
+
return element.getAttribute(name);
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/shared/elementId.ts
|
|
346
|
+
var PROJECT_SOURCE_ID_PATTERN = /^(.+\.(?:vue|tsx|jsx|ts|js)):(\d+):(\d+)$/;
|
|
347
|
+
var RUNTIME_ID_PATTERN = /^runtime:([A-Za-z0-9_-]+)$/;
|
|
348
|
+
var PACKAGE_ID_PREFIX = "pkg:";
|
|
349
|
+
function parseElementId(elementId) {
|
|
350
|
+
const sourceMatch = PROJECT_SOURCE_ID_PATTERN.exec(elementId);
|
|
351
|
+
if (sourceMatch) {
|
|
352
|
+
return {
|
|
353
|
+
kind: "project-source",
|
|
354
|
+
elementId,
|
|
355
|
+
file: sourceMatch[1],
|
|
356
|
+
line: Number(sourceMatch[2]),
|
|
357
|
+
column: Number(sourceMatch[3])
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (elementId.startsWith(PACKAGE_ID_PREFIX)) {
|
|
361
|
+
return parsePackageElementId(elementId);
|
|
362
|
+
}
|
|
363
|
+
const runtimeMatch = RUNTIME_ID_PATTERN.exec(elementId);
|
|
364
|
+
if (runtimeMatch) {
|
|
365
|
+
return {
|
|
366
|
+
kind: "runtime",
|
|
367
|
+
elementId,
|
|
368
|
+
runtimeId: runtimeMatch[1]
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
kind: "invalid",
|
|
373
|
+
elementId,
|
|
374
|
+
reason: "unsupported elementId format"
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function parsePackageElementId(elementId) {
|
|
378
|
+
const value = elementId.slice(PACKAGE_ID_PREFIX.length);
|
|
379
|
+
const parts = value.split("/").filter(Boolean);
|
|
380
|
+
if (parts.length < 2) {
|
|
381
|
+
return {
|
|
382
|
+
kind: "invalid",
|
|
383
|
+
elementId,
|
|
384
|
+
reason: "package elementId must include packageName and entryFile"
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const scoped = parts[0]?.startsWith("@");
|
|
388
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
389
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
390
|
+
if (!entryFile) {
|
|
391
|
+
return {
|
|
392
|
+
kind: "invalid",
|
|
393
|
+
elementId,
|
|
394
|
+
reason: "package elementId must include entryFile"
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
kind: "package",
|
|
399
|
+
elementId,
|
|
400
|
+
packageName,
|
|
401
|
+
entryFile
|
|
402
|
+
};
|
|
403
|
+
}
|
|
174
404
|
|
|
175
405
|
// src/shared/sanitize.ts
|
|
176
406
|
function truncateText(text, maxLength) {
|
|
@@ -195,6 +425,270 @@ function maskHeaders(headers = {}, maskNames = []) {
|
|
|
195
425
|
);
|
|
196
426
|
}
|
|
197
427
|
|
|
428
|
+
// src/runtime/domSnapshot.ts
|
|
429
|
+
function createDomSnapshot(root, options) {
|
|
430
|
+
let count = 0;
|
|
431
|
+
function visit(node, depth) {
|
|
432
|
+
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
436
|
+
return createTextSnapshot(node, options, () => {
|
|
437
|
+
count += 1;
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (!(node instanceof Element)) {
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
const tag = node.tagName.toLowerCase();
|
|
444
|
+
if (["script", "style", "noscript"].includes(tag) || isInternalMcpElement(node)) {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
count += 1;
|
|
448
|
+
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
449
|
+
}
|
|
450
|
+
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
451
|
+
}
|
|
452
|
+
function queryDomElements(selector, limit) {
|
|
453
|
+
return Array.from(document.querySelectorAll(selector)).filter((element) => !isInternalMcpElement(element)).slice(0, limit).map((element) => createDomElementSummary(element));
|
|
454
|
+
}
|
|
455
|
+
function createDomElementSummary(element) {
|
|
456
|
+
return {
|
|
457
|
+
tag: element.tagName.toLowerCase(),
|
|
458
|
+
text: element.textContent.trim(),
|
|
459
|
+
attrs: collectAttrs(element),
|
|
460
|
+
rect: serializeRect(element.getBoundingClientRect())
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function createTextSnapshot(node, options, markVisited) {
|
|
464
|
+
const text = node.textContent?.trim();
|
|
465
|
+
if (!text) {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
markVisited();
|
|
469
|
+
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
470
|
+
}
|
|
471
|
+
function createElementSnapshot(node, tag, visitChild) {
|
|
472
|
+
const attrs = collectAttrs(node);
|
|
473
|
+
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
474
|
+
return {
|
|
475
|
+
tag,
|
|
476
|
+
...node.textContent.trim() ? { text: node.textContent.trim() } : {},
|
|
477
|
+
...Object.keys(attrs).length ? { attrs } : {},
|
|
478
|
+
...children.length ? { children } : {}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function collectAttrs(element) {
|
|
482
|
+
const attrs = {};
|
|
483
|
+
for (const attr of Array.from(element.attributes)) {
|
|
484
|
+
attrs[attr.name] = attr.value;
|
|
485
|
+
}
|
|
486
|
+
if (typeof HTMLInputElement !== "undefined" && element instanceof HTMLInputElement && element.type === "password") {
|
|
487
|
+
attrs.value = "[masked]";
|
|
488
|
+
}
|
|
489
|
+
return attrs;
|
|
490
|
+
}
|
|
491
|
+
function serializeRect(rect) {
|
|
492
|
+
return {
|
|
493
|
+
x: rect.x,
|
|
494
|
+
y: rect.y,
|
|
495
|
+
width: rect.width,
|
|
496
|
+
height: rect.height,
|
|
497
|
+
top: rect.top,
|
|
498
|
+
right: rect.right,
|
|
499
|
+
bottom: rect.bottom,
|
|
500
|
+
left: rect.left
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function isInternalMcpElement(element) {
|
|
504
|
+
return element.getAttribute("data-v-mcp-internal") === "true";
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/runtime/vueComponentLocator.ts
|
|
508
|
+
function locateVueComponentForElement(element, root) {
|
|
509
|
+
const component = findNearestComponent(element);
|
|
510
|
+
if (!component) {
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
const file = getComponentFile(component);
|
|
514
|
+
const name = getComponentName(component);
|
|
515
|
+
if (!file) {
|
|
516
|
+
return { name };
|
|
517
|
+
}
|
|
518
|
+
const packageLocation = parseNodeModulesFile(file);
|
|
519
|
+
if (packageLocation) {
|
|
520
|
+
return {
|
|
521
|
+
name,
|
|
522
|
+
source: void 0,
|
|
523
|
+
packageLocation
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
name,
|
|
528
|
+
source: {
|
|
529
|
+
file: createProjectRelativePath(root, file)
|
|
530
|
+
},
|
|
531
|
+
packageLocation: void 0
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function findNearestComponent(element) {
|
|
535
|
+
let current = element;
|
|
536
|
+
while (current) {
|
|
537
|
+
const component = current.__vueParentComponent;
|
|
538
|
+
if (isVueRuntimeComponent(component)) {
|
|
539
|
+
return component;
|
|
540
|
+
}
|
|
541
|
+
current = current.parentElement;
|
|
542
|
+
}
|
|
543
|
+
return void 0;
|
|
544
|
+
}
|
|
545
|
+
function getComponentName(component) {
|
|
546
|
+
return component.type?.name ?? component.type?.__name ?? component.type?.displayName;
|
|
547
|
+
}
|
|
548
|
+
function getComponentFile(component) {
|
|
549
|
+
return component.type?.__file;
|
|
550
|
+
}
|
|
551
|
+
function parseNodeModulesFile(file) {
|
|
552
|
+
const normalized = normalizePath(file);
|
|
553
|
+
const marker = "/node_modules/";
|
|
554
|
+
const index = normalized.lastIndexOf(marker);
|
|
555
|
+
if (index < 0) {
|
|
556
|
+
return void 0;
|
|
557
|
+
}
|
|
558
|
+
const parts = normalized.slice(index + marker.length).split("/").filter(Boolean);
|
|
559
|
+
const scoped = parts[0]?.startsWith("@");
|
|
560
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
561
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
562
|
+
if (!packageName || !entryFile) {
|
|
563
|
+
return void 0;
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
packageName,
|
|
567
|
+
entryFile
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function normalizePath(path) {
|
|
571
|
+
return path.replace(/\\/g, "/");
|
|
572
|
+
}
|
|
573
|
+
function createProjectRelativePath(root, file) {
|
|
574
|
+
const normalizedRoot = normalizePath(root).replace(/\/$/, "");
|
|
575
|
+
const normalizedFile = normalizePath(file);
|
|
576
|
+
const prefix = `${normalizedRoot}/`;
|
|
577
|
+
if (normalizedFile.startsWith(prefix)) {
|
|
578
|
+
return normalizedFile.slice(prefix.length);
|
|
579
|
+
}
|
|
580
|
+
return normalizedFile.replace(/^\//, "");
|
|
581
|
+
}
|
|
582
|
+
function isVueRuntimeComponent(value) {
|
|
583
|
+
return Boolean(value && typeof value === "object" && "type" in value);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/runtime/elementContext.ts
|
|
587
|
+
var activeResolver;
|
|
588
|
+
function createElementContextResolver(options) {
|
|
589
|
+
return {
|
|
590
|
+
getElementContext(elementId) {
|
|
591
|
+
const parsed = parseElementId(elementId);
|
|
592
|
+
if (parsed.kind === "project-source") {
|
|
593
|
+
const element = options.querySelector(
|
|
594
|
+
`[data-v-mcp-id="${escapeSelector(elementId)}"]`
|
|
595
|
+
);
|
|
596
|
+
return createProjectSourceContext(parsed, element, options.root);
|
|
597
|
+
}
|
|
598
|
+
if (parsed.kind === "package") {
|
|
599
|
+
return createPackageContext(parsed);
|
|
600
|
+
}
|
|
601
|
+
if (parsed.kind === "runtime") {
|
|
602
|
+
const record = options.registry.get(elementId);
|
|
603
|
+
if (!record) {
|
|
604
|
+
return createMissingRuntimeElementError(elementId);
|
|
605
|
+
}
|
|
606
|
+
return createRuntimeContext(elementId, record.element, options.root);
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
ok: false,
|
|
610
|
+
error: parsed.reason,
|
|
611
|
+
elementId,
|
|
612
|
+
limitations: ["please provide a copied elementId from the element picker"]
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function setElementContextResolver(resolver) {
|
|
618
|
+
activeResolver = resolver;
|
|
619
|
+
}
|
|
620
|
+
function getElementContextResolver() {
|
|
621
|
+
activeResolver ??= createElementContextResolver({
|
|
622
|
+
root: "/",
|
|
623
|
+
registry: runtimeElementRegistry,
|
|
624
|
+
querySelector(selector) {
|
|
625
|
+
return document.querySelector(selector);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
return activeResolver;
|
|
629
|
+
}
|
|
630
|
+
function createProjectSourceContext(parsed, element, root) {
|
|
631
|
+
return {
|
|
632
|
+
ok: true,
|
|
633
|
+
elementId: parsed.elementId,
|
|
634
|
+
editable: true,
|
|
635
|
+
codeLocation: {
|
|
636
|
+
file: parsed.file,
|
|
637
|
+
line: parsed.line,
|
|
638
|
+
column: parsed.column
|
|
639
|
+
},
|
|
640
|
+
...element ? { component: locateVueComponentForElement(element, root) } : {},
|
|
641
|
+
...element ? { dom: createDomElementSummary(element) } : {},
|
|
642
|
+
limitations: element ? [] : ["runtime DOM element was not found"]
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function createPackageContext(parsed) {
|
|
646
|
+
return {
|
|
647
|
+
ok: true,
|
|
648
|
+
elementId: parsed.elementId,
|
|
649
|
+
editable: false,
|
|
650
|
+
packageLocation: {
|
|
651
|
+
packageName: parsed.packageName,
|
|
652
|
+
entryFile: parsed.entryFile
|
|
653
|
+
},
|
|
654
|
+
limitations: ["third-party package source is not editable from this project"]
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function createRuntimeContext(elementId, element, root) {
|
|
658
|
+
const component = locateVueComponentForElement(element, root);
|
|
659
|
+
const sourceFile = component?.source?.file;
|
|
660
|
+
return {
|
|
661
|
+
ok: true,
|
|
662
|
+
elementId,
|
|
663
|
+
editable: Boolean(sourceFile),
|
|
664
|
+
...sourceFile ? { codeLocation: { file: sourceFile, line: 1, column: 1 } } : {},
|
|
665
|
+
component,
|
|
666
|
+
dom: createDomElementSummary(element),
|
|
667
|
+
limitations: sourceFile ? ["runtime id maps to nearest component file, exact template node is unavailable"] : ["runtime id is only valid during the current page lifecycle"]
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function createMissingRuntimeElementError(elementId) {
|
|
671
|
+
return {
|
|
672
|
+
ok: false,
|
|
673
|
+
error: "element not found",
|
|
674
|
+
elementId,
|
|
675
|
+
limitations: [
|
|
676
|
+
"element was removed or page refreshed",
|
|
677
|
+
"please ask the user to pick the element again"
|
|
678
|
+
]
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function escapeSelector(value) {
|
|
682
|
+
const css = globalThis.CSS;
|
|
683
|
+
if (css?.escape) {
|
|
684
|
+
return css.escape(value);
|
|
685
|
+
}
|
|
686
|
+
return value.replace(/["\\]/g, "\\$&");
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// src/runtime/networkHook.ts
|
|
690
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
691
|
+
|
|
198
692
|
// src/shared/url.ts
|
|
199
693
|
function parseRequestQuery(url) {
|
|
200
694
|
const parsed = new URL(url, "http://vite-plugin-vue-mcp-next.local");
|
|
@@ -224,7 +718,7 @@ function safeUrlPathname(url) {
|
|
|
224
718
|
// src/runtime/networkHook.ts
|
|
225
719
|
function createHookNetworkRecord(input) {
|
|
226
720
|
return {
|
|
227
|
-
id:
|
|
721
|
+
id: nanoid3(),
|
|
228
722
|
pageId: input.pageId,
|
|
229
723
|
source: "hook",
|
|
230
724
|
url: input.url,
|
|
@@ -379,12 +873,12 @@ function safeReadXhrResponseText(xhr) {
|
|
|
379
873
|
}
|
|
380
874
|
|
|
381
875
|
// src/runtime/pageIdentity.ts
|
|
382
|
-
import { nanoid as
|
|
876
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
383
877
|
var RUNTIME_CLIENT_ID_STORAGE_KEY = "vite-plugin-vue-mcp-next:runtime-client-id";
|
|
384
878
|
var RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX = "vite-plugin-vue-mcp-next:runtime-client-id=";
|
|
385
879
|
var RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR = "\n";
|
|
386
880
|
function createRuntimeClientId() {
|
|
387
|
-
return `runtime-client-${
|
|
881
|
+
return `runtime-client-${nanoid4()}`;
|
|
388
882
|
}
|
|
389
883
|
function readRuntimeClientIdFromTabScope(tabScope) {
|
|
390
884
|
if (!tabScope) {
|
|
@@ -433,7 +927,7 @@ function getRuntimeClientId(storage, tabScope) {
|
|
|
433
927
|
}
|
|
434
928
|
}
|
|
435
929
|
function createRuntimePageId() {
|
|
436
|
-
return `runtime-${
|
|
930
|
+
return `runtime-${nanoid4()}`;
|
|
437
931
|
}
|
|
438
932
|
function getRuntimePageIdentity(input) {
|
|
439
933
|
return {
|
|
@@ -453,7 +947,7 @@ function getRuntimePageIdentity(input) {
|
|
|
453
947
|
}
|
|
454
948
|
|
|
455
949
|
// src/runtime/performanceHook.ts
|
|
456
|
-
import { nanoid as
|
|
950
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
457
951
|
|
|
458
952
|
// src/performance/summary.ts
|
|
459
953
|
function buildPerformanceSummary(input) {
|
|
@@ -627,7 +1121,7 @@ function createPerformanceCollector(deps) {
|
|
|
627
1121
|
};
|
|
628
1122
|
}
|
|
629
1123
|
function createRecordingId() {
|
|
630
|
-
return `performance-${
|
|
1124
|
+
return `performance-${nanoid5()}`;
|
|
631
1125
|
}
|
|
632
1126
|
function startSession(state, deps, options) {
|
|
633
1127
|
const recordingId = createRecordingId();
|
|
@@ -1002,78 +1496,6 @@ import {
|
|
|
1002
1496
|
} from "@vue/devtools-kit";
|
|
1003
1497
|
import { createRPCClient } from "vite-dev-rpc";
|
|
1004
1498
|
|
|
1005
|
-
// src/runtime/domSnapshot.ts
|
|
1006
|
-
function createDomSnapshot(root, options) {
|
|
1007
|
-
let count = 0;
|
|
1008
|
-
function visit(node, depth) {
|
|
1009
|
-
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
1010
|
-
return null;
|
|
1011
|
-
}
|
|
1012
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
1013
|
-
return createTextSnapshot(node, options, () => {
|
|
1014
|
-
count += 1;
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
if (!(node instanceof Element)) {
|
|
1018
|
-
return null;
|
|
1019
|
-
}
|
|
1020
|
-
const tag = node.tagName.toLowerCase();
|
|
1021
|
-
if (["script", "style", "noscript"].includes(tag)) {
|
|
1022
|
-
return null;
|
|
1023
|
-
}
|
|
1024
|
-
count += 1;
|
|
1025
|
-
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
1026
|
-
}
|
|
1027
|
-
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
1028
|
-
}
|
|
1029
|
-
function queryDomElements(selector, limit) {
|
|
1030
|
-
return Array.from(document.querySelectorAll(selector)).slice(0, limit).map((element) => ({
|
|
1031
|
-
tag: element.tagName.toLowerCase(),
|
|
1032
|
-
text: element.textContent.trim(),
|
|
1033
|
-
attrs: collectAttrs(element),
|
|
1034
|
-
rect: serializeRect(element.getBoundingClientRect())
|
|
1035
|
-
}));
|
|
1036
|
-
}
|
|
1037
|
-
function createTextSnapshot(node, options, markVisited) {
|
|
1038
|
-
const text = node.textContent?.trim();
|
|
1039
|
-
if (!text) {
|
|
1040
|
-
return null;
|
|
1041
|
-
}
|
|
1042
|
-
markVisited();
|
|
1043
|
-
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
1044
|
-
}
|
|
1045
|
-
function createElementSnapshot(node, tag, visitChild) {
|
|
1046
|
-
const attrs = collectAttrs(node);
|
|
1047
|
-
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
1048
|
-
return {
|
|
1049
|
-
tag,
|
|
1050
|
-
...Object.keys(attrs).length ? { attrs } : {},
|
|
1051
|
-
...children.length ? { children } : {}
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
function collectAttrs(element) {
|
|
1055
|
-
const attrs = {};
|
|
1056
|
-
for (const attr of Array.from(element.attributes)) {
|
|
1057
|
-
attrs[attr.name] = attr.value;
|
|
1058
|
-
}
|
|
1059
|
-
if (element instanceof HTMLInputElement && element.type === "password") {
|
|
1060
|
-
attrs.value = "[masked]";
|
|
1061
|
-
}
|
|
1062
|
-
return attrs;
|
|
1063
|
-
}
|
|
1064
|
-
function serializeRect(rect) {
|
|
1065
|
-
return {
|
|
1066
|
-
x: rect.x,
|
|
1067
|
-
y: rect.y,
|
|
1068
|
-
width: rect.width,
|
|
1069
|
-
height: rect.height,
|
|
1070
|
-
top: rect.top,
|
|
1071
|
-
right: rect.right,
|
|
1072
|
-
bottom: rect.bottom,
|
|
1073
|
-
left: rect.left
|
|
1074
|
-
};
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
1499
|
// src/runtime/evaluateExpression.ts
|
|
1078
1500
|
async function evaluateExpression(request) {
|
|
1079
1501
|
const value = runExpression(request.expression);
|
|
@@ -1096,6 +1518,390 @@ function createTimeout(timeoutMs) {
|
|
|
1096
1518
|
});
|
|
1097
1519
|
}
|
|
1098
1520
|
|
|
1521
|
+
// src/runtime/storageBridge.ts
|
|
1522
|
+
function createRuntimeStorageBridge(env) {
|
|
1523
|
+
return {
|
|
1524
|
+
async manageStorage(request) {
|
|
1525
|
+
try {
|
|
1526
|
+
return await manageRuntimeStorage(env, request);
|
|
1527
|
+
} catch (error) {
|
|
1528
|
+
return createRuntimeStorageError(
|
|
1529
|
+
request,
|
|
1530
|
+
error instanceof Error ? error.message : String(error)
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
async function manageRuntimeStorage(env, request) {
|
|
1537
|
+
if (request.origin !== env.origin) {
|
|
1538
|
+
return createRuntimeStorageError(
|
|
1539
|
+
request,
|
|
1540
|
+
"Runtime storage access is limited to the current page origin"
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
if (request.scope === "cookie") {
|
|
1544
|
+
return manageRuntimeCookie(env, request);
|
|
1545
|
+
}
|
|
1546
|
+
if (request.scope === "indexedDB") {
|
|
1547
|
+
return manageRuntimeIndexedDb(env, request);
|
|
1548
|
+
}
|
|
1549
|
+
return manageRuntimeWebStorage(getWebStorage(env, request.scope), request);
|
|
1550
|
+
}
|
|
1551
|
+
function manageRuntimeCookie(env, request) {
|
|
1552
|
+
if (!env.cookie) {
|
|
1553
|
+
return createRuntimeStorageError(
|
|
1554
|
+
request,
|
|
1555
|
+
"Runtime cookie access is unavailable",
|
|
1556
|
+
["document.cookie is not available in this runtime environment"]
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
if (request.action === "list") {
|
|
1560
|
+
return createRuntimeStorageSuccess(request, {
|
|
1561
|
+
origin: request.origin,
|
|
1562
|
+
cookies: readRuntimeCookies(env.cookie)
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
if (request.action === "get") {
|
|
1566
|
+
assertCookieName(request);
|
|
1567
|
+
return createRuntimeStorageSuccess(request, {
|
|
1568
|
+
origin: request.origin,
|
|
1569
|
+
cookies: readRuntimeCookies(env.cookie).filter(
|
|
1570
|
+
(cookie) => cookie.name === request.cookie.name
|
|
1571
|
+
)
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
if (request.action === "set") {
|
|
1575
|
+
assertCookieName(request);
|
|
1576
|
+
env.cookie.set(createRuntimeCookieWrite(request));
|
|
1577
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1578
|
+
}
|
|
1579
|
+
if (request.action === "delete") {
|
|
1580
|
+
assertCookieName(request);
|
|
1581
|
+
env.cookie.set(createRuntimeCookieDelete(request));
|
|
1582
|
+
return createRuntimeStorageSuccess(request, {
|
|
1583
|
+
deletedCount: 1,
|
|
1584
|
+
skippedHttpOnlyCount: 0,
|
|
1585
|
+
limitations: ["HttpOnly cookies are invisible to runtime cookie access"]
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
const cookies = readRuntimeCookies(env.cookie);
|
|
1589
|
+
for (const cookie of cookies) {
|
|
1590
|
+
env.cookie.set(
|
|
1591
|
+
createRuntimeCookieDelete({
|
|
1592
|
+
...request,
|
|
1593
|
+
cookie: {
|
|
1594
|
+
name: cookie.name,
|
|
1595
|
+
path: request.cookie?.path
|
|
1596
|
+
}
|
|
1597
|
+
})
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
return createRuntimeStorageSuccess(request, {
|
|
1601
|
+
deletedCount: cookies.length,
|
|
1602
|
+
skippedHttpOnlyCount: 0,
|
|
1603
|
+
limitations: ["HttpOnly cookies are invisible to runtime cookie access"]
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
function getWebStorage(env, scope) {
|
|
1607
|
+
return scope === "sessionStorage" ? env.sessionStorage : env.localStorage;
|
|
1608
|
+
}
|
|
1609
|
+
function manageRuntimeWebStorage(storage, request) {
|
|
1610
|
+
if (request.action === "list") {
|
|
1611
|
+
return createRuntimeStorageSuccess(request, {
|
|
1612
|
+
origin: request.origin,
|
|
1613
|
+
scope: request.scope,
|
|
1614
|
+
entries: readStorageEntries(storage)
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
if (request.action === "get") {
|
|
1618
|
+
assertStorageKey(request);
|
|
1619
|
+
return createRuntimeStorageSuccess(request, {
|
|
1620
|
+
origin: request.origin,
|
|
1621
|
+
scope: request.scope,
|
|
1622
|
+
key: request.key,
|
|
1623
|
+
value: storage.getItem(request.key)
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
if (request.action === "set") {
|
|
1627
|
+
assertStorageKey(request);
|
|
1628
|
+
storage.setItem(request.key, request.value ?? "");
|
|
1629
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1630
|
+
}
|
|
1631
|
+
if (request.action === "delete") {
|
|
1632
|
+
assertStorageKey(request);
|
|
1633
|
+
storage.removeItem(request.key);
|
|
1634
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1635
|
+
}
|
|
1636
|
+
storage.clear();
|
|
1637
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1638
|
+
}
|
|
1639
|
+
async function manageRuntimeIndexedDb(env, request) {
|
|
1640
|
+
if (!env.indexedDB?.databases) {
|
|
1641
|
+
return createRuntimeStorageError(request, "IndexedDB metadata API is unavailable");
|
|
1642
|
+
}
|
|
1643
|
+
if (request.action === "list") {
|
|
1644
|
+
const databases = await env.indexedDB.databases();
|
|
1645
|
+
return createRuntimeStorageSuccess(request, {
|
|
1646
|
+
origin: request.origin,
|
|
1647
|
+
scope: request.scope,
|
|
1648
|
+
databases: databases.map((database) => ({
|
|
1649
|
+
name: database.name,
|
|
1650
|
+
version: database.version
|
|
1651
|
+
}))
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
assertIndexedDbTarget(request);
|
|
1655
|
+
if ("stores" in env.indexedDB) {
|
|
1656
|
+
return manageMemoryIndexedDb(env.indexedDB, request);
|
|
1657
|
+
}
|
|
1658
|
+
return manageBrowserIndexedDb(env.indexedDB, request);
|
|
1659
|
+
}
|
|
1660
|
+
function manageMemoryIndexedDb(indexedDB, request) {
|
|
1661
|
+
const storeId = `${request.databaseName}:${request.objectStoreName}`;
|
|
1662
|
+
const store = indexedDB.stores.get(storeId) ?? /* @__PURE__ */ new Map();
|
|
1663
|
+
indexedDB.stores.set(storeId, store);
|
|
1664
|
+
if (request.action === "get") {
|
|
1665
|
+
assertStorageKey(request);
|
|
1666
|
+
return createRuntimeStorageSuccess(request, {
|
|
1667
|
+
key: request.key,
|
|
1668
|
+
value: store.get(request.key) ?? null
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
if (request.action === "set") {
|
|
1672
|
+
assertStorageKey(request);
|
|
1673
|
+
store.set(request.key, parseIndexedDbValue(request.value));
|
|
1674
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1675
|
+
}
|
|
1676
|
+
if (request.action === "delete") {
|
|
1677
|
+
assertStorageKey(request);
|
|
1678
|
+
store.delete(request.key);
|
|
1679
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1680
|
+
}
|
|
1681
|
+
store.clear();
|
|
1682
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1683
|
+
}
|
|
1684
|
+
async function manageBrowserIndexedDb(indexedDB, request) {
|
|
1685
|
+
const database = await openIndexedDbForRequest(indexedDB, request);
|
|
1686
|
+
try {
|
|
1687
|
+
return await executeIndexedDbTransaction(database, request);
|
|
1688
|
+
} finally {
|
|
1689
|
+
database.close();
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
function openIndexedDb(indexedDB, databaseName) {
|
|
1693
|
+
return new Promise((resolve, reject) => {
|
|
1694
|
+
const request = indexedDB.open(databaseName);
|
|
1695
|
+
request.onerror = () => {
|
|
1696
|
+
reject(request.error ?? new Error("IndexedDB open failed"));
|
|
1697
|
+
};
|
|
1698
|
+
request.onsuccess = () => {
|
|
1699
|
+
resolve(request.result);
|
|
1700
|
+
};
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
async function openIndexedDbForRequest(indexedDB, request) {
|
|
1704
|
+
const database = await openIndexedDb(indexedDB, request.databaseName);
|
|
1705
|
+
if (request.action !== "set" || database.objectStoreNames.contains(request.objectStoreName)) {
|
|
1706
|
+
return database;
|
|
1707
|
+
}
|
|
1708
|
+
const version = database.version + 1;
|
|
1709
|
+
database.close();
|
|
1710
|
+
return openIndexedDbWithStore(indexedDB, {
|
|
1711
|
+
databaseName: request.databaseName,
|
|
1712
|
+
objectStoreName: request.objectStoreName,
|
|
1713
|
+
version
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
function openIndexedDbWithStore(indexedDB, options) {
|
|
1717
|
+
return new Promise((resolve, reject) => {
|
|
1718
|
+
const request = indexedDB.open(options.databaseName, options.version);
|
|
1719
|
+
request.onupgradeneeded = () => {
|
|
1720
|
+
const database = request.result;
|
|
1721
|
+
if (!database.objectStoreNames.contains(options.objectStoreName)) {
|
|
1722
|
+
database.createObjectStore(options.objectStoreName);
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
request.onerror = () => {
|
|
1726
|
+
reject(request.error ?? new Error("IndexedDB upgrade failed"));
|
|
1727
|
+
};
|
|
1728
|
+
request.onsuccess = () => {
|
|
1729
|
+
resolve(request.result);
|
|
1730
|
+
};
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
function executeIndexedDbTransaction(database, request) {
|
|
1734
|
+
return new Promise((resolve, reject) => {
|
|
1735
|
+
const mode = request.action === "get" ? "readonly" : "readwrite";
|
|
1736
|
+
const transaction = database.transaction(request.objectStoreName, mode);
|
|
1737
|
+
const store = transaction.objectStore(request.objectStoreName);
|
|
1738
|
+
transaction.onerror = () => {
|
|
1739
|
+
reject(transaction.error ?? new Error("IndexedDB transaction failed"));
|
|
1740
|
+
};
|
|
1741
|
+
if (request.action === "get") {
|
|
1742
|
+
assertStorageKey(request);
|
|
1743
|
+
const getRequest = store.get(request.key);
|
|
1744
|
+
getRequest.onerror = () => {
|
|
1745
|
+
reject(getRequest.error ?? new Error("IndexedDB get failed"));
|
|
1746
|
+
};
|
|
1747
|
+
getRequest.onsuccess = () => {
|
|
1748
|
+
const value = getRequest.result ?? null;
|
|
1749
|
+
resolve(
|
|
1750
|
+
createRuntimeStorageSuccess(request, {
|
|
1751
|
+
key: request.key,
|
|
1752
|
+
value
|
|
1753
|
+
})
|
|
1754
|
+
);
|
|
1755
|
+
};
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
if (request.action === "set") {
|
|
1759
|
+
assertStorageKey(request);
|
|
1760
|
+
store.put(parseIndexedDbValue(request.value), request.key);
|
|
1761
|
+
transaction.oncomplete = () => {
|
|
1762
|
+
resolve(createRuntimeStorageSuccess(request, { ok: true }));
|
|
1763
|
+
};
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
if (request.action === "delete") {
|
|
1767
|
+
assertStorageKey(request);
|
|
1768
|
+
store.delete(request.key);
|
|
1769
|
+
transaction.oncomplete = () => {
|
|
1770
|
+
resolve(createRuntimeStorageSuccess(request, { ok: true }));
|
|
1771
|
+
};
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
store.clear();
|
|
1775
|
+
transaction.oncomplete = () => {
|
|
1776
|
+
resolve(createRuntimeStorageSuccess(request, { ok: true }));
|
|
1777
|
+
};
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
function readStorageEntries(storage) {
|
|
1781
|
+
const entries = [];
|
|
1782
|
+
for (let index = 0; index < storage.length; index += 1) {
|
|
1783
|
+
const key = storage.key(index);
|
|
1784
|
+
if (!key) {
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
const value = storage.getItem(key);
|
|
1788
|
+
if (value === null) {
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
entries.push({ key, value });
|
|
1792
|
+
}
|
|
1793
|
+
return entries;
|
|
1794
|
+
}
|
|
1795
|
+
function readRuntimeCookies(cookieAccess) {
|
|
1796
|
+
return cookieAccess.get().split(";").map((item) => item.trim()).filter(Boolean).map((item) => {
|
|
1797
|
+
const separatorIndex = item.indexOf("=");
|
|
1798
|
+
if (separatorIndex === -1) {
|
|
1799
|
+
return { name: decodeCookiePart(item), value: "" };
|
|
1800
|
+
}
|
|
1801
|
+
return {
|
|
1802
|
+
name: decodeCookiePart(item.slice(0, separatorIndex)),
|
|
1803
|
+
value: decodeCookiePart(item.slice(separatorIndex + 1))
|
|
1804
|
+
};
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
function createRuntimeCookieWrite(request) {
|
|
1808
|
+
const value = request.cookie.value ?? request.value ?? "";
|
|
1809
|
+
const parts = [
|
|
1810
|
+
`${encodeURIComponent(request.cookie.name)}=${encodeURIComponent(value)}`
|
|
1811
|
+
];
|
|
1812
|
+
appendRuntimeCookieAttributes(parts, request.cookie);
|
|
1813
|
+
return parts.join("; ");
|
|
1814
|
+
}
|
|
1815
|
+
function createRuntimeCookieDelete(request) {
|
|
1816
|
+
const parts = [
|
|
1817
|
+
`${encodeURIComponent(request.cookie.name)}=`,
|
|
1818
|
+
"Expires=Thu, 01 Jan 1970 00:00:00 GMT",
|
|
1819
|
+
"Max-Age=0"
|
|
1820
|
+
];
|
|
1821
|
+
appendRuntimeCookieAttributes(parts, {
|
|
1822
|
+
path: request.cookie.path,
|
|
1823
|
+
domain: request.cookie.domain
|
|
1824
|
+
});
|
|
1825
|
+
return parts.join("; ");
|
|
1826
|
+
}
|
|
1827
|
+
function appendRuntimeCookieAttributes(parts, cookie) {
|
|
1828
|
+
if (cookie.path) {
|
|
1829
|
+
parts.push(`Path=${cookie.path}`);
|
|
1830
|
+
}
|
|
1831
|
+
if (cookie.domain) {
|
|
1832
|
+
parts.push(`Domain=${cookie.domain}`);
|
|
1833
|
+
}
|
|
1834
|
+
if (cookie.expires !== void 0) {
|
|
1835
|
+
parts.push(`Expires=${new Date(cookie.expires * 1e3).toUTCString()}`);
|
|
1836
|
+
}
|
|
1837
|
+
if (cookie.sameSite) {
|
|
1838
|
+
parts.push(`SameSite=${normalizeRuntimeSameSite(cookie.sameSite)}`);
|
|
1839
|
+
}
|
|
1840
|
+
if (cookie.secure) {
|
|
1841
|
+
parts.push("Secure");
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
function normalizeRuntimeSameSite(sameSite) {
|
|
1845
|
+
if (sameSite === "strict") {
|
|
1846
|
+
return "Strict";
|
|
1847
|
+
}
|
|
1848
|
+
if (sameSite === "lax") {
|
|
1849
|
+
return "Lax";
|
|
1850
|
+
}
|
|
1851
|
+
return "None";
|
|
1852
|
+
}
|
|
1853
|
+
function decodeCookiePart(value) {
|
|
1854
|
+
try {
|
|
1855
|
+
return decodeURIComponent(value);
|
|
1856
|
+
} catch {
|
|
1857
|
+
return value;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
function assertStorageKey(request) {
|
|
1861
|
+
if (!request.key) {
|
|
1862
|
+
throw new Error("Storage key is required for this operation");
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
function assertCookieName(request) {
|
|
1866
|
+
if (!request.cookie?.name) {
|
|
1867
|
+
throw new Error("Cookie name is required for this operation");
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
function assertIndexedDbTarget(request) {
|
|
1871
|
+
if (!request.databaseName || !request.objectStoreName) {
|
|
1872
|
+
throw new Error("IndexedDB databaseName and objectStoreName are required");
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
function parseIndexedDbValue(value) {
|
|
1876
|
+
if (value === void 0) {
|
|
1877
|
+
return null;
|
|
1878
|
+
}
|
|
1879
|
+
try {
|
|
1880
|
+
return JSON.parse(value);
|
|
1881
|
+
} catch {
|
|
1882
|
+
return value;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
function createRuntimeStorageSuccess(request, data) {
|
|
1886
|
+
return {
|
|
1887
|
+
ok: true,
|
|
1888
|
+
source: "hook",
|
|
1889
|
+
action: request.action,
|
|
1890
|
+
scope: request.scope,
|
|
1891
|
+
data
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
function createRuntimeStorageError(request, error, limitations) {
|
|
1895
|
+
return {
|
|
1896
|
+
ok: false,
|
|
1897
|
+
source: "hook",
|
|
1898
|
+
action: request.action,
|
|
1899
|
+
scope: request.scope,
|
|
1900
|
+
error,
|
|
1901
|
+
limitations
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1099
1905
|
// src/runtime/devtoolsBridge.ts
|
|
1100
1906
|
function createRuntimeDevtoolsRpc(getRpc) {
|
|
1101
1907
|
return {
|
|
@@ -1117,6 +1923,22 @@ function createRuntimeDevtoolsRpc(getRpc) {
|
|
|
1117
1923
|
);
|
|
1118
1924
|
},
|
|
1119
1925
|
onDomQueryUpdated: () => void 0,
|
|
1926
|
+
getElementContext(options) {
|
|
1927
|
+
try {
|
|
1928
|
+
getRpc().onElementContextUpdated(
|
|
1929
|
+
options.event,
|
|
1930
|
+
getElementContextResolver().getElementContext(options.elementId)
|
|
1931
|
+
);
|
|
1932
|
+
} catch (error) {
|
|
1933
|
+
getRpc().onElementContextUpdated(options.event, {
|
|
1934
|
+
ok: false,
|
|
1935
|
+
elementId: options.elementId,
|
|
1936
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1937
|
+
limitations: ["runtime element context lookup failed"]
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
},
|
|
1941
|
+
onElementContextUpdated: () => void 0,
|
|
1120
1942
|
reloadPage(options) {
|
|
1121
1943
|
getRpc().onPageReloaded(options.event, { ok: true, source: "hook" });
|
|
1122
1944
|
setTimeout(() => {
|
|
@@ -1151,7 +1973,26 @@ function createRuntimeDevtoolsRpc(getRpc) {
|
|
|
1151
1973
|
});
|
|
1152
1974
|
}
|
|
1153
1975
|
},
|
|
1154
|
-
onScreenshotTaken: () => void 0
|
|
1976
|
+
onScreenshotTaken: () => void 0,
|
|
1977
|
+
async manageStorage(options) {
|
|
1978
|
+
const bridge = createRuntimeStorageBridge({
|
|
1979
|
+
origin: window.location.origin,
|
|
1980
|
+
localStorage: window.localStorage,
|
|
1981
|
+
sessionStorage: window.sessionStorage,
|
|
1982
|
+
indexedDB: window.indexedDB,
|
|
1983
|
+
cookie: {
|
|
1984
|
+
get: () => window.document.cookie,
|
|
1985
|
+
set: (value) => {
|
|
1986
|
+
window.document.cookie = value;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
});
|
|
1990
|
+
getRpc().onStorageUpdated(
|
|
1991
|
+
options.event,
|
|
1992
|
+
await bridge.manageStorage(options)
|
|
1993
|
+
);
|
|
1994
|
+
},
|
|
1995
|
+
onStorageUpdated: () => void 0
|
|
1155
1996
|
};
|
|
1156
1997
|
}
|
|
1157
1998
|
|
|
@@ -1434,13 +2275,16 @@ function createMissingComponentError(componentName) {
|
|
|
1434
2275
|
}
|
|
1435
2276
|
|
|
1436
2277
|
// src/runtime/client.ts
|
|
1437
|
-
async function startRuntimeClient(
|
|
2278
|
+
async function startRuntimeClient(runtimeOptions = {
|
|
2279
|
+
elementPicker: DEFAULT_OPTIONS.elementPicker
|
|
2280
|
+
}) {
|
|
1438
2281
|
initializeVueDevtoolsHook();
|
|
1439
2282
|
const hot = await createHotContext("vite-plugin-vue-mcp-next", "/");
|
|
1440
2283
|
if (!hot) {
|
|
1441
2284
|
return;
|
|
1442
2285
|
}
|
|
1443
2286
|
installVueBridge(hot);
|
|
2287
|
+
installElementPicker(runtimeOptions.elementPicker);
|
|
1444
2288
|
const identity = getRuntimePageIdentity({
|
|
1445
2289
|
href: window.location.href,
|
|
1446
2290
|
title: document.title,
|
|
@@ -1449,6 +2293,15 @@ async function startRuntimeClient() {
|
|
|
1449
2293
|
innerHeight: window.innerHeight,
|
|
1450
2294
|
readyState: document.readyState
|
|
1451
2295
|
});
|
|
2296
|
+
setElementContextResolver(
|
|
2297
|
+
createElementContextResolver({
|
|
2298
|
+
root: runtimeOptions.projectRoot ?? "/",
|
|
2299
|
+
registry: runtimeElementRegistry,
|
|
2300
|
+
querySelector(selector) {
|
|
2301
|
+
return document.querySelector(selector);
|
|
2302
|
+
}
|
|
2303
|
+
})
|
|
2304
|
+
);
|
|
1452
2305
|
hot.send(RUNTIME_PAGE_CONNECTED_EVENT, identity);
|
|
1453
2306
|
installRuntimePageLifecycle({
|
|
1454
2307
|
pageId: identity.pageId,
|