@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.cjs
CHANGED
|
@@ -60,6 +60,7 @@ var RUNTIME_PAGE_CONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-connected";
|
|
|
60
60
|
var RUNTIME_PAGE_DISCONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-disconnected";
|
|
61
61
|
var RUNTIME_PAGE_HEARTBEAT_EVENT = "vite-plugin-vue-mcp-next:heartbeat";
|
|
62
62
|
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
63
|
+
var DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS = 2200;
|
|
63
64
|
var DEFAULT_OPTIONS = {
|
|
64
65
|
mcpPath: DEFAULT_MCP_PATH,
|
|
65
66
|
host: "localhost",
|
|
@@ -78,6 +79,16 @@ var DEFAULT_OPTIONS = {
|
|
|
78
79
|
skill: {
|
|
79
80
|
autoConfig: true
|
|
80
81
|
},
|
|
82
|
+
elementPicker: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
shortcut: {
|
|
85
|
+
altKey: true,
|
|
86
|
+
shiftKey: true,
|
|
87
|
+
metaKey: false,
|
|
88
|
+
ctrlKey: false
|
|
89
|
+
},
|
|
90
|
+
toastDurationMs: DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS
|
|
91
|
+
},
|
|
81
92
|
runtime: {
|
|
82
93
|
mode: "auto",
|
|
83
94
|
evaluate: {
|
|
@@ -196,8 +207,227 @@ function installConsoleHook(options) {
|
|
|
196
207
|
};
|
|
197
208
|
}
|
|
198
209
|
|
|
199
|
-
// src/runtime/
|
|
210
|
+
// src/runtime/elementRegistry.ts
|
|
200
211
|
var import_nanoid2 = require("nanoid");
|
|
212
|
+
var RUNTIME_ELEMENT_ID_PREFIX = "runtime:vmcp_";
|
|
213
|
+
var RUNTIME_ELEMENT_ID_SIZE = 8;
|
|
214
|
+
var runtimeElementRegistry = createRuntimeElementRegistry();
|
|
215
|
+
function createRuntimeElementId() {
|
|
216
|
+
return `${RUNTIME_ELEMENT_ID_PREFIX}${(0, import_nanoid2.nanoid)(RUNTIME_ELEMENT_ID_SIZE)}`;
|
|
217
|
+
}
|
|
218
|
+
function createRuntimeElementRegistry() {
|
|
219
|
+
const records = /* @__PURE__ */ new Map();
|
|
220
|
+
return {
|
|
221
|
+
register(element) {
|
|
222
|
+
const elementId = createRuntimeElementId();
|
|
223
|
+
records.set(elementId, {
|
|
224
|
+
elementId,
|
|
225
|
+
element,
|
|
226
|
+
createdAt: Date.now()
|
|
227
|
+
});
|
|
228
|
+
return elementId;
|
|
229
|
+
},
|
|
230
|
+
get(elementId) {
|
|
231
|
+
return records.get(elementId);
|
|
232
|
+
},
|
|
233
|
+
clear() {
|
|
234
|
+
records.clear();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/runtime/elementPicker.ts
|
|
240
|
+
var MCP_ID_ATTR = "data-v-mcp-id";
|
|
241
|
+
var INTERNAL_ATTR = "data-v-mcp-internal";
|
|
242
|
+
var SUCCESS_MESSAGE = "\u5143\u7D20\u4F4D\u7F6E\u5DF2\u590D\u5236\uFF0C\u8BF7\u53D1\u9001\u7ED9 AI";
|
|
243
|
+
var COPY_FAILED_PREFIX = "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u5143\u7D20 ID";
|
|
244
|
+
var OVERLAY_Z_INDEX = "2147483647";
|
|
245
|
+
var TOAST_Z_INDEX = "2147483647";
|
|
246
|
+
function installElementPicker(options) {
|
|
247
|
+
if (!options.enabled) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const registry = runtimeElementRegistry;
|
|
251
|
+
const overlay = createOverlay();
|
|
252
|
+
let active = false;
|
|
253
|
+
let currentElement;
|
|
254
|
+
window.addEventListener("keydown", (event) => {
|
|
255
|
+
active = matchesShortcut(event, options.shortcut);
|
|
256
|
+
});
|
|
257
|
+
window.addEventListener("keyup", () => {
|
|
258
|
+
active = false;
|
|
259
|
+
currentElement = void 0;
|
|
260
|
+
updateOverlay(overlay);
|
|
261
|
+
});
|
|
262
|
+
window.addEventListener("mousemove", (event) => {
|
|
263
|
+
if (!active) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
currentElement = document.elementFromPoint(event.clientX, event.clientY) ?? void 0;
|
|
267
|
+
updateOverlay(overlay, currentElement);
|
|
268
|
+
});
|
|
269
|
+
window.addEventListener(
|
|
270
|
+
"click",
|
|
271
|
+
(event) => {
|
|
272
|
+
if (!active || !isElementLike(event.target)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
event.stopPropagation();
|
|
277
|
+
void copyAndNotify(event.target, registry, options.toastDurationMs);
|
|
278
|
+
},
|
|
279
|
+
true
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
function matchesShortcut(event, shortcut) {
|
|
283
|
+
return event.altKey === shortcut.altKey && event.shiftKey === shortcut.shiftKey && event.metaKey === shortcut.metaKey && event.ctrlKey === shortcut.ctrlKey;
|
|
284
|
+
}
|
|
285
|
+
function resolveElementId(element, registry) {
|
|
286
|
+
return element.getAttribute(MCP_ID_ATTR) ?? registry.register(element);
|
|
287
|
+
}
|
|
288
|
+
async function copyAndNotify(element, registry, toastDurationMs) {
|
|
289
|
+
const elementId = resolveElementId(element, registry);
|
|
290
|
+
const copied = await copyElementId(elementId);
|
|
291
|
+
showToast(
|
|
292
|
+
copied ? SUCCESS_MESSAGE : `${COPY_FAILED_PREFIX}: ${elementId}`,
|
|
293
|
+
toastDurationMs
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
async function copyElementId(elementId) {
|
|
297
|
+
try {
|
|
298
|
+
await navigator.clipboard.writeText(elementId);
|
|
299
|
+
return true;
|
|
300
|
+
} catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function createOverlay() {
|
|
305
|
+
const overlay = document.createElement("div");
|
|
306
|
+
markInternalElement(overlay);
|
|
307
|
+
Object.assign(overlay.style, {
|
|
308
|
+
position: "fixed",
|
|
309
|
+
pointerEvents: "none",
|
|
310
|
+
border: "2px solid #1d4ed8",
|
|
311
|
+
background: "rgba(29, 78, 216, 0.08)",
|
|
312
|
+
zIndex: OVERLAY_Z_INDEX,
|
|
313
|
+
display: "none"
|
|
314
|
+
});
|
|
315
|
+
document.body.appendChild(overlay);
|
|
316
|
+
return overlay;
|
|
317
|
+
}
|
|
318
|
+
function updateOverlay(overlay, element) {
|
|
319
|
+
if (!element || getElementAttr(element, INTERNAL_ATTR) === "true") {
|
|
320
|
+
overlay.style.display = "none";
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const rect = element.getBoundingClientRect();
|
|
324
|
+
Object.assign(overlay.style, {
|
|
325
|
+
display: "block",
|
|
326
|
+
left: `${String(rect.x)}px`,
|
|
327
|
+
top: `${String(rect.y)}px`,
|
|
328
|
+
width: `${String(rect.width)}px`,
|
|
329
|
+
height: `${String(rect.height)}px`
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function showToast(message, durationMs) {
|
|
333
|
+
const toast = document.createElement("div");
|
|
334
|
+
markInternalElement(toast);
|
|
335
|
+
toast.textContent = message;
|
|
336
|
+
Object.assign(toast.style, {
|
|
337
|
+
position: "fixed",
|
|
338
|
+
left: "50%",
|
|
339
|
+
bottom: "32px",
|
|
340
|
+
transform: "translateX(-50%)",
|
|
341
|
+
zIndex: TOAST_Z_INDEX,
|
|
342
|
+
padding: "8px 12px",
|
|
343
|
+
borderRadius: "6px",
|
|
344
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
345
|
+
color: "#fff",
|
|
346
|
+
fontSize: "13px",
|
|
347
|
+
pointerEvents: "none"
|
|
348
|
+
});
|
|
349
|
+
document.body.appendChild(toast);
|
|
350
|
+
globalThis.setTimeout(() => {
|
|
351
|
+
toast.remove();
|
|
352
|
+
}, durationMs);
|
|
353
|
+
}
|
|
354
|
+
function isElementLike(value) {
|
|
355
|
+
return Boolean(
|
|
356
|
+
value && typeof value === "object" && "getAttribute" in value && "getBoundingClientRect" in value
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
function markInternalElement(element) {
|
|
360
|
+
if (typeof element.setAttribute === "function") {
|
|
361
|
+
element.setAttribute(INTERNAL_ATTR, "true");
|
|
362
|
+
}
|
|
363
|
+
element.dataset.vMcpInternal = "true";
|
|
364
|
+
}
|
|
365
|
+
function getElementAttr(element, name) {
|
|
366
|
+
if (typeof element.getAttribute === "function") {
|
|
367
|
+
return element.getAttribute(name);
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/shared/elementId.ts
|
|
373
|
+
var PROJECT_SOURCE_ID_PATTERN = /^(.+\.(?:vue|tsx|jsx|ts|js)):(\d+):(\d+)$/;
|
|
374
|
+
var RUNTIME_ID_PATTERN = /^runtime:([A-Za-z0-9_-]+)$/;
|
|
375
|
+
var PACKAGE_ID_PREFIX = "pkg:";
|
|
376
|
+
function parseElementId(elementId) {
|
|
377
|
+
const sourceMatch = PROJECT_SOURCE_ID_PATTERN.exec(elementId);
|
|
378
|
+
if (sourceMatch) {
|
|
379
|
+
return {
|
|
380
|
+
kind: "project-source",
|
|
381
|
+
elementId,
|
|
382
|
+
file: sourceMatch[1],
|
|
383
|
+
line: Number(sourceMatch[2]),
|
|
384
|
+
column: Number(sourceMatch[3])
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
if (elementId.startsWith(PACKAGE_ID_PREFIX)) {
|
|
388
|
+
return parsePackageElementId(elementId);
|
|
389
|
+
}
|
|
390
|
+
const runtimeMatch = RUNTIME_ID_PATTERN.exec(elementId);
|
|
391
|
+
if (runtimeMatch) {
|
|
392
|
+
return {
|
|
393
|
+
kind: "runtime",
|
|
394
|
+
elementId,
|
|
395
|
+
runtimeId: runtimeMatch[1]
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
kind: "invalid",
|
|
400
|
+
elementId,
|
|
401
|
+
reason: "unsupported elementId format"
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function parsePackageElementId(elementId) {
|
|
405
|
+
const value = elementId.slice(PACKAGE_ID_PREFIX.length);
|
|
406
|
+
const parts = value.split("/").filter(Boolean);
|
|
407
|
+
if (parts.length < 2) {
|
|
408
|
+
return {
|
|
409
|
+
kind: "invalid",
|
|
410
|
+
elementId,
|
|
411
|
+
reason: "package elementId must include packageName and entryFile"
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const scoped = parts[0]?.startsWith("@");
|
|
415
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
416
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
417
|
+
if (!entryFile) {
|
|
418
|
+
return {
|
|
419
|
+
kind: "invalid",
|
|
420
|
+
elementId,
|
|
421
|
+
reason: "package elementId must include entryFile"
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
kind: "package",
|
|
426
|
+
elementId,
|
|
427
|
+
packageName,
|
|
428
|
+
entryFile
|
|
429
|
+
};
|
|
430
|
+
}
|
|
201
431
|
|
|
202
432
|
// src/shared/sanitize.ts
|
|
203
433
|
function truncateText(text, maxLength) {
|
|
@@ -222,6 +452,270 @@ function maskHeaders(headers = {}, maskNames = []) {
|
|
|
222
452
|
);
|
|
223
453
|
}
|
|
224
454
|
|
|
455
|
+
// src/runtime/domSnapshot.ts
|
|
456
|
+
function createDomSnapshot(root, options) {
|
|
457
|
+
let count = 0;
|
|
458
|
+
function visit(node, depth) {
|
|
459
|
+
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
463
|
+
return createTextSnapshot(node, options, () => {
|
|
464
|
+
count += 1;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
if (!(node instanceof Element)) {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
const tag = node.tagName.toLowerCase();
|
|
471
|
+
if (["script", "style", "noscript"].includes(tag) || isInternalMcpElement(node)) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
count += 1;
|
|
475
|
+
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
476
|
+
}
|
|
477
|
+
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
478
|
+
}
|
|
479
|
+
function queryDomElements(selector, limit) {
|
|
480
|
+
return Array.from(document.querySelectorAll(selector)).filter((element) => !isInternalMcpElement(element)).slice(0, limit).map((element) => createDomElementSummary(element));
|
|
481
|
+
}
|
|
482
|
+
function createDomElementSummary(element) {
|
|
483
|
+
return {
|
|
484
|
+
tag: element.tagName.toLowerCase(),
|
|
485
|
+
text: element.textContent.trim(),
|
|
486
|
+
attrs: collectAttrs(element),
|
|
487
|
+
rect: serializeRect(element.getBoundingClientRect())
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function createTextSnapshot(node, options, markVisited) {
|
|
491
|
+
const text = node.textContent?.trim();
|
|
492
|
+
if (!text) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
markVisited();
|
|
496
|
+
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
497
|
+
}
|
|
498
|
+
function createElementSnapshot(node, tag, visitChild) {
|
|
499
|
+
const attrs = collectAttrs(node);
|
|
500
|
+
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
501
|
+
return {
|
|
502
|
+
tag,
|
|
503
|
+
...node.textContent.trim() ? { text: node.textContent.trim() } : {},
|
|
504
|
+
...Object.keys(attrs).length ? { attrs } : {},
|
|
505
|
+
...children.length ? { children } : {}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function collectAttrs(element) {
|
|
509
|
+
const attrs = {};
|
|
510
|
+
for (const attr of Array.from(element.attributes)) {
|
|
511
|
+
attrs[attr.name] = attr.value;
|
|
512
|
+
}
|
|
513
|
+
if (typeof HTMLInputElement !== "undefined" && element instanceof HTMLInputElement && element.type === "password") {
|
|
514
|
+
attrs.value = "[masked]";
|
|
515
|
+
}
|
|
516
|
+
return attrs;
|
|
517
|
+
}
|
|
518
|
+
function serializeRect(rect) {
|
|
519
|
+
return {
|
|
520
|
+
x: rect.x,
|
|
521
|
+
y: rect.y,
|
|
522
|
+
width: rect.width,
|
|
523
|
+
height: rect.height,
|
|
524
|
+
top: rect.top,
|
|
525
|
+
right: rect.right,
|
|
526
|
+
bottom: rect.bottom,
|
|
527
|
+
left: rect.left
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function isInternalMcpElement(element) {
|
|
531
|
+
return element.getAttribute("data-v-mcp-internal") === "true";
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/runtime/vueComponentLocator.ts
|
|
535
|
+
function locateVueComponentForElement(element, root) {
|
|
536
|
+
const component = findNearestComponent(element);
|
|
537
|
+
if (!component) {
|
|
538
|
+
return void 0;
|
|
539
|
+
}
|
|
540
|
+
const file = getComponentFile(component);
|
|
541
|
+
const name = getComponentName(component);
|
|
542
|
+
if (!file) {
|
|
543
|
+
return { name };
|
|
544
|
+
}
|
|
545
|
+
const packageLocation = parseNodeModulesFile(file);
|
|
546
|
+
if (packageLocation) {
|
|
547
|
+
return {
|
|
548
|
+
name,
|
|
549
|
+
source: void 0,
|
|
550
|
+
packageLocation
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
name,
|
|
555
|
+
source: {
|
|
556
|
+
file: createProjectRelativePath(root, file)
|
|
557
|
+
},
|
|
558
|
+
packageLocation: void 0
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function findNearestComponent(element) {
|
|
562
|
+
let current = element;
|
|
563
|
+
while (current) {
|
|
564
|
+
const component = current.__vueParentComponent;
|
|
565
|
+
if (isVueRuntimeComponent(component)) {
|
|
566
|
+
return component;
|
|
567
|
+
}
|
|
568
|
+
current = current.parentElement;
|
|
569
|
+
}
|
|
570
|
+
return void 0;
|
|
571
|
+
}
|
|
572
|
+
function getComponentName(component) {
|
|
573
|
+
return component.type?.name ?? component.type?.__name ?? component.type?.displayName;
|
|
574
|
+
}
|
|
575
|
+
function getComponentFile(component) {
|
|
576
|
+
return component.type?.__file;
|
|
577
|
+
}
|
|
578
|
+
function parseNodeModulesFile(file) {
|
|
579
|
+
const normalized = normalizePath(file);
|
|
580
|
+
const marker = "/node_modules/";
|
|
581
|
+
const index = normalized.lastIndexOf(marker);
|
|
582
|
+
if (index < 0) {
|
|
583
|
+
return void 0;
|
|
584
|
+
}
|
|
585
|
+
const parts = normalized.slice(index + marker.length).split("/").filter(Boolean);
|
|
586
|
+
const scoped = parts[0]?.startsWith("@");
|
|
587
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
588
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
589
|
+
if (!packageName || !entryFile) {
|
|
590
|
+
return void 0;
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
packageName,
|
|
594
|
+
entryFile
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function normalizePath(path) {
|
|
598
|
+
return path.replace(/\\/g, "/");
|
|
599
|
+
}
|
|
600
|
+
function createProjectRelativePath(root, file) {
|
|
601
|
+
const normalizedRoot = normalizePath(root).replace(/\/$/, "");
|
|
602
|
+
const normalizedFile = normalizePath(file);
|
|
603
|
+
const prefix = `${normalizedRoot}/`;
|
|
604
|
+
if (normalizedFile.startsWith(prefix)) {
|
|
605
|
+
return normalizedFile.slice(prefix.length);
|
|
606
|
+
}
|
|
607
|
+
return normalizedFile.replace(/^\//, "");
|
|
608
|
+
}
|
|
609
|
+
function isVueRuntimeComponent(value) {
|
|
610
|
+
return Boolean(value && typeof value === "object" && "type" in value);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/runtime/elementContext.ts
|
|
614
|
+
var activeResolver;
|
|
615
|
+
function createElementContextResolver(options) {
|
|
616
|
+
return {
|
|
617
|
+
getElementContext(elementId) {
|
|
618
|
+
const parsed = parseElementId(elementId);
|
|
619
|
+
if (parsed.kind === "project-source") {
|
|
620
|
+
const element = options.querySelector(
|
|
621
|
+
`[data-v-mcp-id="${escapeSelector(elementId)}"]`
|
|
622
|
+
);
|
|
623
|
+
return createProjectSourceContext(parsed, element, options.root);
|
|
624
|
+
}
|
|
625
|
+
if (parsed.kind === "package") {
|
|
626
|
+
return createPackageContext(parsed);
|
|
627
|
+
}
|
|
628
|
+
if (parsed.kind === "runtime") {
|
|
629
|
+
const record = options.registry.get(elementId);
|
|
630
|
+
if (!record) {
|
|
631
|
+
return createMissingRuntimeElementError(elementId);
|
|
632
|
+
}
|
|
633
|
+
return createRuntimeContext(elementId, record.element, options.root);
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
ok: false,
|
|
637
|
+
error: parsed.reason,
|
|
638
|
+
elementId,
|
|
639
|
+
limitations: ["please provide a copied elementId from the element picker"]
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function setElementContextResolver(resolver) {
|
|
645
|
+
activeResolver = resolver;
|
|
646
|
+
}
|
|
647
|
+
function getElementContextResolver() {
|
|
648
|
+
activeResolver ??= createElementContextResolver({
|
|
649
|
+
root: "/",
|
|
650
|
+
registry: runtimeElementRegistry,
|
|
651
|
+
querySelector(selector) {
|
|
652
|
+
return document.querySelector(selector);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
return activeResolver;
|
|
656
|
+
}
|
|
657
|
+
function createProjectSourceContext(parsed, element, root) {
|
|
658
|
+
return {
|
|
659
|
+
ok: true,
|
|
660
|
+
elementId: parsed.elementId,
|
|
661
|
+
editable: true,
|
|
662
|
+
codeLocation: {
|
|
663
|
+
file: parsed.file,
|
|
664
|
+
line: parsed.line,
|
|
665
|
+
column: parsed.column
|
|
666
|
+
},
|
|
667
|
+
...element ? { component: locateVueComponentForElement(element, root) } : {},
|
|
668
|
+
...element ? { dom: createDomElementSummary(element) } : {},
|
|
669
|
+
limitations: element ? [] : ["runtime DOM element was not found"]
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function createPackageContext(parsed) {
|
|
673
|
+
return {
|
|
674
|
+
ok: true,
|
|
675
|
+
elementId: parsed.elementId,
|
|
676
|
+
editable: false,
|
|
677
|
+
packageLocation: {
|
|
678
|
+
packageName: parsed.packageName,
|
|
679
|
+
entryFile: parsed.entryFile
|
|
680
|
+
},
|
|
681
|
+
limitations: ["third-party package source is not editable from this project"]
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function createRuntimeContext(elementId, element, root) {
|
|
685
|
+
const component = locateVueComponentForElement(element, root);
|
|
686
|
+
const sourceFile = component?.source?.file;
|
|
687
|
+
return {
|
|
688
|
+
ok: true,
|
|
689
|
+
elementId,
|
|
690
|
+
editable: Boolean(sourceFile),
|
|
691
|
+
...sourceFile ? { codeLocation: { file: sourceFile, line: 1, column: 1 } } : {},
|
|
692
|
+
component,
|
|
693
|
+
dom: createDomElementSummary(element),
|
|
694
|
+
limitations: sourceFile ? ["runtime id maps to nearest component file, exact template node is unavailable"] : ["runtime id is only valid during the current page lifecycle"]
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function createMissingRuntimeElementError(elementId) {
|
|
698
|
+
return {
|
|
699
|
+
ok: false,
|
|
700
|
+
error: "element not found",
|
|
701
|
+
elementId,
|
|
702
|
+
limitations: [
|
|
703
|
+
"element was removed or page refreshed",
|
|
704
|
+
"please ask the user to pick the element again"
|
|
705
|
+
]
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function escapeSelector(value) {
|
|
709
|
+
const css = globalThis.CSS;
|
|
710
|
+
if (css?.escape) {
|
|
711
|
+
return css.escape(value);
|
|
712
|
+
}
|
|
713
|
+
return value.replace(/["\\]/g, "\\$&");
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/runtime/networkHook.ts
|
|
717
|
+
var import_nanoid3 = require("nanoid");
|
|
718
|
+
|
|
225
719
|
// src/shared/url.ts
|
|
226
720
|
function parseRequestQuery(url) {
|
|
227
721
|
const parsed = new URL(url, "http://vite-plugin-vue-mcp-next.local");
|
|
@@ -251,7 +745,7 @@ function safeUrlPathname(url) {
|
|
|
251
745
|
// src/runtime/networkHook.ts
|
|
252
746
|
function createHookNetworkRecord(input) {
|
|
253
747
|
return {
|
|
254
|
-
id: (0,
|
|
748
|
+
id: (0, import_nanoid3.nanoid)(),
|
|
255
749
|
pageId: input.pageId,
|
|
256
750
|
source: "hook",
|
|
257
751
|
url: input.url,
|
|
@@ -406,12 +900,12 @@ function safeReadXhrResponseText(xhr) {
|
|
|
406
900
|
}
|
|
407
901
|
|
|
408
902
|
// src/runtime/pageIdentity.ts
|
|
409
|
-
var
|
|
903
|
+
var import_nanoid4 = require("nanoid");
|
|
410
904
|
var RUNTIME_CLIENT_ID_STORAGE_KEY = "vite-plugin-vue-mcp-next:runtime-client-id";
|
|
411
905
|
var RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX = "vite-plugin-vue-mcp-next:runtime-client-id=";
|
|
412
906
|
var RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR = "\n";
|
|
413
907
|
function createRuntimeClientId() {
|
|
414
|
-
return `runtime-client-${(0,
|
|
908
|
+
return `runtime-client-${(0, import_nanoid4.nanoid)()}`;
|
|
415
909
|
}
|
|
416
910
|
function readRuntimeClientIdFromTabScope(tabScope) {
|
|
417
911
|
if (!tabScope) {
|
|
@@ -460,7 +954,7 @@ function getRuntimeClientId(storage, tabScope) {
|
|
|
460
954
|
}
|
|
461
955
|
}
|
|
462
956
|
function createRuntimePageId() {
|
|
463
|
-
return `runtime-${(0,
|
|
957
|
+
return `runtime-${(0, import_nanoid4.nanoid)()}`;
|
|
464
958
|
}
|
|
465
959
|
function getRuntimePageIdentity(input) {
|
|
466
960
|
return {
|
|
@@ -480,7 +974,7 @@ function getRuntimePageIdentity(input) {
|
|
|
480
974
|
}
|
|
481
975
|
|
|
482
976
|
// src/runtime/performanceHook.ts
|
|
483
|
-
var
|
|
977
|
+
var import_nanoid5 = require("nanoid");
|
|
484
978
|
|
|
485
979
|
// src/performance/summary.ts
|
|
486
980
|
function buildPerformanceSummary(input) {
|
|
@@ -654,7 +1148,7 @@ function createPerformanceCollector(deps) {
|
|
|
654
1148
|
};
|
|
655
1149
|
}
|
|
656
1150
|
function createRecordingId() {
|
|
657
|
-
return `performance-${(0,
|
|
1151
|
+
return `performance-${(0, import_nanoid5.nanoid)()}`;
|
|
658
1152
|
}
|
|
659
1153
|
function startSession(state, deps, options) {
|
|
660
1154
|
const recordingId = createRecordingId();
|
|
@@ -1022,78 +1516,6 @@ function createMissingSnapdomError() {
|
|
|
1022
1516
|
var import_devtools_kit = require("@vue/devtools-kit");
|
|
1023
1517
|
var import_vite_dev_rpc = require("vite-dev-rpc");
|
|
1024
1518
|
|
|
1025
|
-
// src/runtime/domSnapshot.ts
|
|
1026
|
-
function createDomSnapshot(root, options) {
|
|
1027
|
-
let count = 0;
|
|
1028
|
-
function visit(node, depth) {
|
|
1029
|
-
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
1030
|
-
return null;
|
|
1031
|
-
}
|
|
1032
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
1033
|
-
return createTextSnapshot(node, options, () => {
|
|
1034
|
-
count += 1;
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
if (!(node instanceof Element)) {
|
|
1038
|
-
return null;
|
|
1039
|
-
}
|
|
1040
|
-
const tag = node.tagName.toLowerCase();
|
|
1041
|
-
if (["script", "style", "noscript"].includes(tag)) {
|
|
1042
|
-
return null;
|
|
1043
|
-
}
|
|
1044
|
-
count += 1;
|
|
1045
|
-
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
1046
|
-
}
|
|
1047
|
-
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
1048
|
-
}
|
|
1049
|
-
function queryDomElements(selector, limit) {
|
|
1050
|
-
return Array.from(document.querySelectorAll(selector)).slice(0, limit).map((element) => ({
|
|
1051
|
-
tag: element.tagName.toLowerCase(),
|
|
1052
|
-
text: element.textContent.trim(),
|
|
1053
|
-
attrs: collectAttrs(element),
|
|
1054
|
-
rect: serializeRect(element.getBoundingClientRect())
|
|
1055
|
-
}));
|
|
1056
|
-
}
|
|
1057
|
-
function createTextSnapshot(node, options, markVisited) {
|
|
1058
|
-
const text = node.textContent?.trim();
|
|
1059
|
-
if (!text) {
|
|
1060
|
-
return null;
|
|
1061
|
-
}
|
|
1062
|
-
markVisited();
|
|
1063
|
-
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
1064
|
-
}
|
|
1065
|
-
function createElementSnapshot(node, tag, visitChild) {
|
|
1066
|
-
const attrs = collectAttrs(node);
|
|
1067
|
-
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
1068
|
-
return {
|
|
1069
|
-
tag,
|
|
1070
|
-
...Object.keys(attrs).length ? { attrs } : {},
|
|
1071
|
-
...children.length ? { children } : {}
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
function collectAttrs(element) {
|
|
1075
|
-
const attrs = {};
|
|
1076
|
-
for (const attr of Array.from(element.attributes)) {
|
|
1077
|
-
attrs[attr.name] = attr.value;
|
|
1078
|
-
}
|
|
1079
|
-
if (element instanceof HTMLInputElement && element.type === "password") {
|
|
1080
|
-
attrs.value = "[masked]";
|
|
1081
|
-
}
|
|
1082
|
-
return attrs;
|
|
1083
|
-
}
|
|
1084
|
-
function serializeRect(rect) {
|
|
1085
|
-
return {
|
|
1086
|
-
x: rect.x,
|
|
1087
|
-
y: rect.y,
|
|
1088
|
-
width: rect.width,
|
|
1089
|
-
height: rect.height,
|
|
1090
|
-
top: rect.top,
|
|
1091
|
-
right: rect.right,
|
|
1092
|
-
bottom: rect.bottom,
|
|
1093
|
-
left: rect.left
|
|
1094
|
-
};
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
1519
|
// src/runtime/evaluateExpression.ts
|
|
1098
1520
|
async function evaluateExpression(request) {
|
|
1099
1521
|
const value = runExpression(request.expression);
|
|
@@ -1116,6 +1538,390 @@ function createTimeout(timeoutMs) {
|
|
|
1116
1538
|
});
|
|
1117
1539
|
}
|
|
1118
1540
|
|
|
1541
|
+
// src/runtime/storageBridge.ts
|
|
1542
|
+
function createRuntimeStorageBridge(env) {
|
|
1543
|
+
return {
|
|
1544
|
+
async manageStorage(request) {
|
|
1545
|
+
try {
|
|
1546
|
+
return await manageRuntimeStorage(env, request);
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
return createRuntimeStorageError(
|
|
1549
|
+
request,
|
|
1550
|
+
error instanceof Error ? error.message : String(error)
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
async function manageRuntimeStorage(env, request) {
|
|
1557
|
+
if (request.origin !== env.origin) {
|
|
1558
|
+
return createRuntimeStorageError(
|
|
1559
|
+
request,
|
|
1560
|
+
"Runtime storage access is limited to the current page origin"
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
if (request.scope === "cookie") {
|
|
1564
|
+
return manageRuntimeCookie(env, request);
|
|
1565
|
+
}
|
|
1566
|
+
if (request.scope === "indexedDB") {
|
|
1567
|
+
return manageRuntimeIndexedDb(env, request);
|
|
1568
|
+
}
|
|
1569
|
+
return manageRuntimeWebStorage(getWebStorage(env, request.scope), request);
|
|
1570
|
+
}
|
|
1571
|
+
function manageRuntimeCookie(env, request) {
|
|
1572
|
+
if (!env.cookie) {
|
|
1573
|
+
return createRuntimeStorageError(
|
|
1574
|
+
request,
|
|
1575
|
+
"Runtime cookie access is unavailable",
|
|
1576
|
+
["document.cookie is not available in this runtime environment"]
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
if (request.action === "list") {
|
|
1580
|
+
return createRuntimeStorageSuccess(request, {
|
|
1581
|
+
origin: request.origin,
|
|
1582
|
+
cookies: readRuntimeCookies(env.cookie)
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
if (request.action === "get") {
|
|
1586
|
+
assertCookieName(request);
|
|
1587
|
+
return createRuntimeStorageSuccess(request, {
|
|
1588
|
+
origin: request.origin,
|
|
1589
|
+
cookies: readRuntimeCookies(env.cookie).filter(
|
|
1590
|
+
(cookie) => cookie.name === request.cookie.name
|
|
1591
|
+
)
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
if (request.action === "set") {
|
|
1595
|
+
assertCookieName(request);
|
|
1596
|
+
env.cookie.set(createRuntimeCookieWrite(request));
|
|
1597
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1598
|
+
}
|
|
1599
|
+
if (request.action === "delete") {
|
|
1600
|
+
assertCookieName(request);
|
|
1601
|
+
env.cookie.set(createRuntimeCookieDelete(request));
|
|
1602
|
+
return createRuntimeStorageSuccess(request, {
|
|
1603
|
+
deletedCount: 1,
|
|
1604
|
+
skippedHttpOnlyCount: 0,
|
|
1605
|
+
limitations: ["HttpOnly cookies are invisible to runtime cookie access"]
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
const cookies = readRuntimeCookies(env.cookie);
|
|
1609
|
+
for (const cookie of cookies) {
|
|
1610
|
+
env.cookie.set(
|
|
1611
|
+
createRuntimeCookieDelete({
|
|
1612
|
+
...request,
|
|
1613
|
+
cookie: {
|
|
1614
|
+
name: cookie.name,
|
|
1615
|
+
path: request.cookie?.path
|
|
1616
|
+
}
|
|
1617
|
+
})
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
return createRuntimeStorageSuccess(request, {
|
|
1621
|
+
deletedCount: cookies.length,
|
|
1622
|
+
skippedHttpOnlyCount: 0,
|
|
1623
|
+
limitations: ["HttpOnly cookies are invisible to runtime cookie access"]
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
function getWebStorage(env, scope) {
|
|
1627
|
+
return scope === "sessionStorage" ? env.sessionStorage : env.localStorage;
|
|
1628
|
+
}
|
|
1629
|
+
function manageRuntimeWebStorage(storage, request) {
|
|
1630
|
+
if (request.action === "list") {
|
|
1631
|
+
return createRuntimeStorageSuccess(request, {
|
|
1632
|
+
origin: request.origin,
|
|
1633
|
+
scope: request.scope,
|
|
1634
|
+
entries: readStorageEntries(storage)
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
if (request.action === "get") {
|
|
1638
|
+
assertStorageKey(request);
|
|
1639
|
+
return createRuntimeStorageSuccess(request, {
|
|
1640
|
+
origin: request.origin,
|
|
1641
|
+
scope: request.scope,
|
|
1642
|
+
key: request.key,
|
|
1643
|
+
value: storage.getItem(request.key)
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
if (request.action === "set") {
|
|
1647
|
+
assertStorageKey(request);
|
|
1648
|
+
storage.setItem(request.key, request.value ?? "");
|
|
1649
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1650
|
+
}
|
|
1651
|
+
if (request.action === "delete") {
|
|
1652
|
+
assertStorageKey(request);
|
|
1653
|
+
storage.removeItem(request.key);
|
|
1654
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1655
|
+
}
|
|
1656
|
+
storage.clear();
|
|
1657
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1658
|
+
}
|
|
1659
|
+
async function manageRuntimeIndexedDb(env, request) {
|
|
1660
|
+
if (!env.indexedDB?.databases) {
|
|
1661
|
+
return createRuntimeStorageError(request, "IndexedDB metadata API is unavailable");
|
|
1662
|
+
}
|
|
1663
|
+
if (request.action === "list") {
|
|
1664
|
+
const databases = await env.indexedDB.databases();
|
|
1665
|
+
return createRuntimeStorageSuccess(request, {
|
|
1666
|
+
origin: request.origin,
|
|
1667
|
+
scope: request.scope,
|
|
1668
|
+
databases: databases.map((database) => ({
|
|
1669
|
+
name: database.name,
|
|
1670
|
+
version: database.version
|
|
1671
|
+
}))
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
assertIndexedDbTarget(request);
|
|
1675
|
+
if ("stores" in env.indexedDB) {
|
|
1676
|
+
return manageMemoryIndexedDb(env.indexedDB, request);
|
|
1677
|
+
}
|
|
1678
|
+
return manageBrowserIndexedDb(env.indexedDB, request);
|
|
1679
|
+
}
|
|
1680
|
+
function manageMemoryIndexedDb(indexedDB, request) {
|
|
1681
|
+
const storeId = `${request.databaseName}:${request.objectStoreName}`;
|
|
1682
|
+
const store = indexedDB.stores.get(storeId) ?? /* @__PURE__ */ new Map();
|
|
1683
|
+
indexedDB.stores.set(storeId, store);
|
|
1684
|
+
if (request.action === "get") {
|
|
1685
|
+
assertStorageKey(request);
|
|
1686
|
+
return createRuntimeStorageSuccess(request, {
|
|
1687
|
+
key: request.key,
|
|
1688
|
+
value: store.get(request.key) ?? null
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
if (request.action === "set") {
|
|
1692
|
+
assertStorageKey(request);
|
|
1693
|
+
store.set(request.key, parseIndexedDbValue(request.value));
|
|
1694
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1695
|
+
}
|
|
1696
|
+
if (request.action === "delete") {
|
|
1697
|
+
assertStorageKey(request);
|
|
1698
|
+
store.delete(request.key);
|
|
1699
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1700
|
+
}
|
|
1701
|
+
store.clear();
|
|
1702
|
+
return createRuntimeStorageSuccess(request, { ok: true });
|
|
1703
|
+
}
|
|
1704
|
+
async function manageBrowserIndexedDb(indexedDB, request) {
|
|
1705
|
+
const database = await openIndexedDbForRequest(indexedDB, request);
|
|
1706
|
+
try {
|
|
1707
|
+
return await executeIndexedDbTransaction(database, request);
|
|
1708
|
+
} finally {
|
|
1709
|
+
database.close();
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
function openIndexedDb(indexedDB, databaseName) {
|
|
1713
|
+
return new Promise((resolve, reject) => {
|
|
1714
|
+
const request = indexedDB.open(databaseName);
|
|
1715
|
+
request.onerror = () => {
|
|
1716
|
+
reject(request.error ?? new Error("IndexedDB open failed"));
|
|
1717
|
+
};
|
|
1718
|
+
request.onsuccess = () => {
|
|
1719
|
+
resolve(request.result);
|
|
1720
|
+
};
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
async function openIndexedDbForRequest(indexedDB, request) {
|
|
1724
|
+
const database = await openIndexedDb(indexedDB, request.databaseName);
|
|
1725
|
+
if (request.action !== "set" || database.objectStoreNames.contains(request.objectStoreName)) {
|
|
1726
|
+
return database;
|
|
1727
|
+
}
|
|
1728
|
+
const version = database.version + 1;
|
|
1729
|
+
database.close();
|
|
1730
|
+
return openIndexedDbWithStore(indexedDB, {
|
|
1731
|
+
databaseName: request.databaseName,
|
|
1732
|
+
objectStoreName: request.objectStoreName,
|
|
1733
|
+
version
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
function openIndexedDbWithStore(indexedDB, options) {
|
|
1737
|
+
return new Promise((resolve, reject) => {
|
|
1738
|
+
const request = indexedDB.open(options.databaseName, options.version);
|
|
1739
|
+
request.onupgradeneeded = () => {
|
|
1740
|
+
const database = request.result;
|
|
1741
|
+
if (!database.objectStoreNames.contains(options.objectStoreName)) {
|
|
1742
|
+
database.createObjectStore(options.objectStoreName);
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
request.onerror = () => {
|
|
1746
|
+
reject(request.error ?? new Error("IndexedDB upgrade failed"));
|
|
1747
|
+
};
|
|
1748
|
+
request.onsuccess = () => {
|
|
1749
|
+
resolve(request.result);
|
|
1750
|
+
};
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
function executeIndexedDbTransaction(database, request) {
|
|
1754
|
+
return new Promise((resolve, reject) => {
|
|
1755
|
+
const mode = request.action === "get" ? "readonly" : "readwrite";
|
|
1756
|
+
const transaction = database.transaction(request.objectStoreName, mode);
|
|
1757
|
+
const store = transaction.objectStore(request.objectStoreName);
|
|
1758
|
+
transaction.onerror = () => {
|
|
1759
|
+
reject(transaction.error ?? new Error("IndexedDB transaction failed"));
|
|
1760
|
+
};
|
|
1761
|
+
if (request.action === "get") {
|
|
1762
|
+
assertStorageKey(request);
|
|
1763
|
+
const getRequest = store.get(request.key);
|
|
1764
|
+
getRequest.onerror = () => {
|
|
1765
|
+
reject(getRequest.error ?? new Error("IndexedDB get failed"));
|
|
1766
|
+
};
|
|
1767
|
+
getRequest.onsuccess = () => {
|
|
1768
|
+
const value = getRequest.result ?? null;
|
|
1769
|
+
resolve(
|
|
1770
|
+
createRuntimeStorageSuccess(request, {
|
|
1771
|
+
key: request.key,
|
|
1772
|
+
value
|
|
1773
|
+
})
|
|
1774
|
+
);
|
|
1775
|
+
};
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
if (request.action === "set") {
|
|
1779
|
+
assertStorageKey(request);
|
|
1780
|
+
store.put(parseIndexedDbValue(request.value), request.key);
|
|
1781
|
+
transaction.oncomplete = () => {
|
|
1782
|
+
resolve(createRuntimeStorageSuccess(request, { ok: true }));
|
|
1783
|
+
};
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
if (request.action === "delete") {
|
|
1787
|
+
assertStorageKey(request);
|
|
1788
|
+
store.delete(request.key);
|
|
1789
|
+
transaction.oncomplete = () => {
|
|
1790
|
+
resolve(createRuntimeStorageSuccess(request, { ok: true }));
|
|
1791
|
+
};
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
store.clear();
|
|
1795
|
+
transaction.oncomplete = () => {
|
|
1796
|
+
resolve(createRuntimeStorageSuccess(request, { ok: true }));
|
|
1797
|
+
};
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
function readStorageEntries(storage) {
|
|
1801
|
+
const entries = [];
|
|
1802
|
+
for (let index = 0; index < storage.length; index += 1) {
|
|
1803
|
+
const key = storage.key(index);
|
|
1804
|
+
if (!key) {
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
const value = storage.getItem(key);
|
|
1808
|
+
if (value === null) {
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
entries.push({ key, value });
|
|
1812
|
+
}
|
|
1813
|
+
return entries;
|
|
1814
|
+
}
|
|
1815
|
+
function readRuntimeCookies(cookieAccess) {
|
|
1816
|
+
return cookieAccess.get().split(";").map((item) => item.trim()).filter(Boolean).map((item) => {
|
|
1817
|
+
const separatorIndex = item.indexOf("=");
|
|
1818
|
+
if (separatorIndex === -1) {
|
|
1819
|
+
return { name: decodeCookiePart(item), value: "" };
|
|
1820
|
+
}
|
|
1821
|
+
return {
|
|
1822
|
+
name: decodeCookiePart(item.slice(0, separatorIndex)),
|
|
1823
|
+
value: decodeCookiePart(item.slice(separatorIndex + 1))
|
|
1824
|
+
};
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
function createRuntimeCookieWrite(request) {
|
|
1828
|
+
const value = request.cookie.value ?? request.value ?? "";
|
|
1829
|
+
const parts = [
|
|
1830
|
+
`${encodeURIComponent(request.cookie.name)}=${encodeURIComponent(value)}`
|
|
1831
|
+
];
|
|
1832
|
+
appendRuntimeCookieAttributes(parts, request.cookie);
|
|
1833
|
+
return parts.join("; ");
|
|
1834
|
+
}
|
|
1835
|
+
function createRuntimeCookieDelete(request) {
|
|
1836
|
+
const parts = [
|
|
1837
|
+
`${encodeURIComponent(request.cookie.name)}=`,
|
|
1838
|
+
"Expires=Thu, 01 Jan 1970 00:00:00 GMT",
|
|
1839
|
+
"Max-Age=0"
|
|
1840
|
+
];
|
|
1841
|
+
appendRuntimeCookieAttributes(parts, {
|
|
1842
|
+
path: request.cookie.path,
|
|
1843
|
+
domain: request.cookie.domain
|
|
1844
|
+
});
|
|
1845
|
+
return parts.join("; ");
|
|
1846
|
+
}
|
|
1847
|
+
function appendRuntimeCookieAttributes(parts, cookie) {
|
|
1848
|
+
if (cookie.path) {
|
|
1849
|
+
parts.push(`Path=${cookie.path}`);
|
|
1850
|
+
}
|
|
1851
|
+
if (cookie.domain) {
|
|
1852
|
+
parts.push(`Domain=${cookie.domain}`);
|
|
1853
|
+
}
|
|
1854
|
+
if (cookie.expires !== void 0) {
|
|
1855
|
+
parts.push(`Expires=${new Date(cookie.expires * 1e3).toUTCString()}`);
|
|
1856
|
+
}
|
|
1857
|
+
if (cookie.sameSite) {
|
|
1858
|
+
parts.push(`SameSite=${normalizeRuntimeSameSite(cookie.sameSite)}`);
|
|
1859
|
+
}
|
|
1860
|
+
if (cookie.secure) {
|
|
1861
|
+
parts.push("Secure");
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
function normalizeRuntimeSameSite(sameSite) {
|
|
1865
|
+
if (sameSite === "strict") {
|
|
1866
|
+
return "Strict";
|
|
1867
|
+
}
|
|
1868
|
+
if (sameSite === "lax") {
|
|
1869
|
+
return "Lax";
|
|
1870
|
+
}
|
|
1871
|
+
return "None";
|
|
1872
|
+
}
|
|
1873
|
+
function decodeCookiePart(value) {
|
|
1874
|
+
try {
|
|
1875
|
+
return decodeURIComponent(value);
|
|
1876
|
+
} catch {
|
|
1877
|
+
return value;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
function assertStorageKey(request) {
|
|
1881
|
+
if (!request.key) {
|
|
1882
|
+
throw new Error("Storage key is required for this operation");
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
function assertCookieName(request) {
|
|
1886
|
+
if (!request.cookie?.name) {
|
|
1887
|
+
throw new Error("Cookie name is required for this operation");
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
function assertIndexedDbTarget(request) {
|
|
1891
|
+
if (!request.databaseName || !request.objectStoreName) {
|
|
1892
|
+
throw new Error("IndexedDB databaseName and objectStoreName are required");
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
function parseIndexedDbValue(value) {
|
|
1896
|
+
if (value === void 0) {
|
|
1897
|
+
return null;
|
|
1898
|
+
}
|
|
1899
|
+
try {
|
|
1900
|
+
return JSON.parse(value);
|
|
1901
|
+
} catch {
|
|
1902
|
+
return value;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
function createRuntimeStorageSuccess(request, data) {
|
|
1906
|
+
return {
|
|
1907
|
+
ok: true,
|
|
1908
|
+
source: "hook",
|
|
1909
|
+
action: request.action,
|
|
1910
|
+
scope: request.scope,
|
|
1911
|
+
data
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
function createRuntimeStorageError(request, error, limitations) {
|
|
1915
|
+
return {
|
|
1916
|
+
ok: false,
|
|
1917
|
+
source: "hook",
|
|
1918
|
+
action: request.action,
|
|
1919
|
+
scope: request.scope,
|
|
1920
|
+
error,
|
|
1921
|
+
limitations
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1119
1925
|
// src/runtime/devtoolsBridge.ts
|
|
1120
1926
|
function createRuntimeDevtoolsRpc(getRpc) {
|
|
1121
1927
|
return {
|
|
@@ -1137,6 +1943,22 @@ function createRuntimeDevtoolsRpc(getRpc) {
|
|
|
1137
1943
|
);
|
|
1138
1944
|
},
|
|
1139
1945
|
onDomQueryUpdated: () => void 0,
|
|
1946
|
+
getElementContext(options) {
|
|
1947
|
+
try {
|
|
1948
|
+
getRpc().onElementContextUpdated(
|
|
1949
|
+
options.event,
|
|
1950
|
+
getElementContextResolver().getElementContext(options.elementId)
|
|
1951
|
+
);
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
getRpc().onElementContextUpdated(options.event, {
|
|
1954
|
+
ok: false,
|
|
1955
|
+
elementId: options.elementId,
|
|
1956
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1957
|
+
limitations: ["runtime element context lookup failed"]
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
},
|
|
1961
|
+
onElementContextUpdated: () => void 0,
|
|
1140
1962
|
reloadPage(options) {
|
|
1141
1963
|
getRpc().onPageReloaded(options.event, { ok: true, source: "hook" });
|
|
1142
1964
|
setTimeout(() => {
|
|
@@ -1171,7 +1993,26 @@ function createRuntimeDevtoolsRpc(getRpc) {
|
|
|
1171
1993
|
});
|
|
1172
1994
|
}
|
|
1173
1995
|
},
|
|
1174
|
-
onScreenshotTaken: () => void 0
|
|
1996
|
+
onScreenshotTaken: () => void 0,
|
|
1997
|
+
async manageStorage(options) {
|
|
1998
|
+
const bridge = createRuntimeStorageBridge({
|
|
1999
|
+
origin: window.location.origin,
|
|
2000
|
+
localStorage: window.localStorage,
|
|
2001
|
+
sessionStorage: window.sessionStorage,
|
|
2002
|
+
indexedDB: window.indexedDB,
|
|
2003
|
+
cookie: {
|
|
2004
|
+
get: () => window.document.cookie,
|
|
2005
|
+
set: (value) => {
|
|
2006
|
+
window.document.cookie = value;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
});
|
|
2010
|
+
getRpc().onStorageUpdated(
|
|
2011
|
+
options.event,
|
|
2012
|
+
await bridge.manageStorage(options)
|
|
2013
|
+
);
|
|
2014
|
+
},
|
|
2015
|
+
onStorageUpdated: () => void 0
|
|
1175
2016
|
};
|
|
1176
2017
|
}
|
|
1177
2018
|
|
|
@@ -1454,13 +2295,16 @@ function createMissingComponentError(componentName) {
|
|
|
1454
2295
|
}
|
|
1455
2296
|
|
|
1456
2297
|
// src/runtime/client.ts
|
|
1457
|
-
async function startRuntimeClient(
|
|
2298
|
+
async function startRuntimeClient(runtimeOptions = {
|
|
2299
|
+
elementPicker: DEFAULT_OPTIONS.elementPicker
|
|
2300
|
+
}) {
|
|
1458
2301
|
initializeVueDevtoolsHook();
|
|
1459
2302
|
const hot = await (0, import_vite_hot_client.createHotContext)("vite-plugin-vue-mcp-next", "/");
|
|
1460
2303
|
if (!hot) {
|
|
1461
2304
|
return;
|
|
1462
2305
|
}
|
|
1463
2306
|
installVueBridge(hot);
|
|
2307
|
+
installElementPicker(runtimeOptions.elementPicker);
|
|
1464
2308
|
const identity = getRuntimePageIdentity({
|
|
1465
2309
|
href: window.location.href,
|
|
1466
2310
|
title: document.title,
|
|
@@ -1469,6 +2313,15 @@ async function startRuntimeClient() {
|
|
|
1469
2313
|
innerHeight: window.innerHeight,
|
|
1470
2314
|
readyState: document.readyState
|
|
1471
2315
|
});
|
|
2316
|
+
setElementContextResolver(
|
|
2317
|
+
createElementContextResolver({
|
|
2318
|
+
root: runtimeOptions.projectRoot ?? "/",
|
|
2319
|
+
registry: runtimeElementRegistry,
|
|
2320
|
+
querySelector(selector) {
|
|
2321
|
+
return document.querySelector(selector);
|
|
2322
|
+
}
|
|
2323
|
+
})
|
|
2324
|
+
);
|
|
1472
2325
|
hot.send(RUNTIME_PAGE_CONNECTED_EVENT, identity);
|
|
1473
2326
|
installRuntimePageLifecycle({
|
|
1474
2327
|
pageId: identity.pageId,
|