@xiaou66/vite-plugin-vue-mcp-next 1.0.3 → 1.1.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 +54 -1
- package/dist/index.cjs +804 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +277 -5
- package/dist/index.d.ts +277 -5
- package/dist/index.js +755 -57
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +639 -1
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.js +639 -1
- package/dist/runtime/client.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/client.cjs
CHANGED
|
@@ -308,6 +308,58 @@ function safeReadXhrResponseText(xhr) {
|
|
|
308
308
|
|
|
309
309
|
// src/runtime/pageIdentity.ts
|
|
310
310
|
var import_nanoid3 = require("nanoid");
|
|
311
|
+
var RUNTIME_CLIENT_ID_STORAGE_KEY = "vite-plugin-vue-mcp-next:runtime-client-id";
|
|
312
|
+
var RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX = "vite-plugin-vue-mcp-next:runtime-client-id=";
|
|
313
|
+
var RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR = "\n";
|
|
314
|
+
function createRuntimeClientId() {
|
|
315
|
+
return `runtime-client-${(0, import_nanoid3.nanoid)()}`;
|
|
316
|
+
}
|
|
317
|
+
function readRuntimeClientIdFromTabScope(tabScope) {
|
|
318
|
+
if (!tabScope) {
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
321
|
+
if (tabScope.__VITE_MCP_NEXT_RUNTIME_CLIENT_ID__) {
|
|
322
|
+
return tabScope.__VITE_MCP_NEXT_RUNTIME_CLIENT_ID__;
|
|
323
|
+
}
|
|
324
|
+
return tabScope.name.split(RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR).find((item) => item.startsWith(RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX))?.slice(RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX.length);
|
|
325
|
+
}
|
|
326
|
+
function writeRuntimeClientIdToTabScope(tabScope, clientId) {
|
|
327
|
+
tabScope.__VITE_MCP_NEXT_RUNTIME_CLIENT_ID__ = clientId;
|
|
328
|
+
const preservedNameParts = tabScope.name.split(RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR).filter((item) => !item.startsWith(RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX)).filter(Boolean);
|
|
329
|
+
tabScope.name = [
|
|
330
|
+
...preservedNameParts,
|
|
331
|
+
`${RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX}${clientId}`
|
|
332
|
+
].join(RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR);
|
|
333
|
+
}
|
|
334
|
+
function persistRuntimeClientId(storage, clientId, tabScope) {
|
|
335
|
+
storage.setItem(RUNTIME_CLIENT_ID_STORAGE_KEY, clientId);
|
|
336
|
+
if (tabScope) {
|
|
337
|
+
writeRuntimeClientIdToTabScope(tabScope, clientId);
|
|
338
|
+
}
|
|
339
|
+
return clientId;
|
|
340
|
+
}
|
|
341
|
+
function getRuntimeClientId(storage, tabScope) {
|
|
342
|
+
const nextClientId = createRuntimeClientId();
|
|
343
|
+
if (!storage) {
|
|
344
|
+
return nextClientId;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const tabScopedClientId = readRuntimeClientIdFromTabScope(tabScope);
|
|
348
|
+
if (tabScopedClientId) {
|
|
349
|
+
return persistRuntimeClientId(storage, tabScopedClientId, tabScope);
|
|
350
|
+
}
|
|
351
|
+
if (tabScope) {
|
|
352
|
+
return persistRuntimeClientId(storage, nextClientId, tabScope);
|
|
353
|
+
}
|
|
354
|
+
const currentClientId = storage.getItem(RUNTIME_CLIENT_ID_STORAGE_KEY);
|
|
355
|
+
if (currentClientId) {
|
|
356
|
+
return currentClientId;
|
|
357
|
+
}
|
|
358
|
+
return persistRuntimeClientId(storage, nextClientId);
|
|
359
|
+
} catch {
|
|
360
|
+
return nextClientId;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
311
363
|
function createRuntimePageId() {
|
|
312
364
|
return `runtime-${(0, import_nanoid3.nanoid)()}`;
|
|
313
365
|
}
|
|
@@ -318,6 +370,7 @@ function getRuntimePageIdentity(input) {
|
|
|
318
370
|
url: input.href,
|
|
319
371
|
pathname: safeUrlPathname(input.href),
|
|
320
372
|
title: input.title,
|
|
373
|
+
runtimeClientId: input.runtimeClientId,
|
|
321
374
|
connected: true,
|
|
322
375
|
readyState: input.readyState,
|
|
323
376
|
viewport: {
|
|
@@ -327,6 +380,500 @@ function getRuntimePageIdentity(input) {
|
|
|
327
380
|
};
|
|
328
381
|
}
|
|
329
382
|
|
|
383
|
+
// src/runtime/performanceHook.ts
|
|
384
|
+
var import_nanoid4 = require("nanoid");
|
|
385
|
+
|
|
386
|
+
// src/shared/limits.ts
|
|
387
|
+
var DEFAULT_DOM_MAX_DEPTH = 8;
|
|
388
|
+
var DEFAULT_DOM_MAX_NODES = 2e3;
|
|
389
|
+
var DEFAULT_DOM_MAX_TEXT_LENGTH = 300;
|
|
390
|
+
var DEFAULT_CONSOLE_MAX_RECORDS = 1e3;
|
|
391
|
+
var DEFAULT_NETWORK_MAX_RECORDS = 500;
|
|
392
|
+
var DEFAULT_NETWORK_MAX_BODY_SIZE = 1e5;
|
|
393
|
+
var DEFAULT_MASK_HEADERS = [
|
|
394
|
+
"authorization",
|
|
395
|
+
"cookie",
|
|
396
|
+
"set-cookie"
|
|
397
|
+
];
|
|
398
|
+
|
|
399
|
+
// src/constants.ts
|
|
400
|
+
var DEFAULT_MCP_PATH = "/__mcp";
|
|
401
|
+
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
402
|
+
var DEFAULT_SCREENSHOT_SAVE_DIR = ".vite-mcp/screenshot";
|
|
403
|
+
var DEFAULT_PERFORMANCE_SAVE_DIR = ".vite-mcp/performance";
|
|
404
|
+
var DEFAULT_PERFORMANCE_MAX_DURATION_MS = 3e4;
|
|
405
|
+
var DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS = 250;
|
|
406
|
+
var DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS = 50;
|
|
407
|
+
var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
|
|
408
|
+
var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
|
|
409
|
+
var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-config";
|
|
410
|
+
var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
|
|
411
|
+
var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
|
|
412
|
+
var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
413
|
+
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
|
|
414
|
+
var DEFAULT_OPTIONS = {
|
|
415
|
+
mcpPath: DEFAULT_MCP_PATH,
|
|
416
|
+
host: "localhost",
|
|
417
|
+
printUrl: true,
|
|
418
|
+
updateCursorMcpJson: {
|
|
419
|
+
enabled: true,
|
|
420
|
+
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
421
|
+
},
|
|
422
|
+
mcpClients: {
|
|
423
|
+
cursor: true,
|
|
424
|
+
codex: true,
|
|
425
|
+
claudeCode: true,
|
|
426
|
+
trae: true,
|
|
427
|
+
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
428
|
+
},
|
|
429
|
+
skill: {
|
|
430
|
+
autoConfig: true
|
|
431
|
+
},
|
|
432
|
+
runtime: {
|
|
433
|
+
mode: "auto",
|
|
434
|
+
evaluate: {
|
|
435
|
+
enabled: false,
|
|
436
|
+
timeoutMs: 3e3
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
cdp: {},
|
|
440
|
+
network: {
|
|
441
|
+
mode: "auto",
|
|
442
|
+
maxRecords: DEFAULT_NETWORK_MAX_RECORDS,
|
|
443
|
+
captureRequestBody: true,
|
|
444
|
+
captureResponseBody: true,
|
|
445
|
+
maxBodySize: DEFAULT_NETWORK_MAX_BODY_SIZE,
|
|
446
|
+
maskHeaders: [...DEFAULT_MASK_HEADERS]
|
|
447
|
+
},
|
|
448
|
+
dom: {
|
|
449
|
+
maxDepth: DEFAULT_DOM_MAX_DEPTH,
|
|
450
|
+
maxNodes: DEFAULT_DOM_MAX_NODES,
|
|
451
|
+
maxTextLength: DEFAULT_DOM_MAX_TEXT_LENGTH
|
|
452
|
+
},
|
|
453
|
+
console: {
|
|
454
|
+
maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
|
|
455
|
+
},
|
|
456
|
+
screenshot: {
|
|
457
|
+
type: "path",
|
|
458
|
+
saveDir: DEFAULT_SCREENSHOT_SAVE_DIR,
|
|
459
|
+
prefer: "auto",
|
|
460
|
+
maxBytes: DEFAULT_SCREENSHOT_MAX_BYTES,
|
|
461
|
+
snapdom: {
|
|
462
|
+
options: {},
|
|
463
|
+
plugins: []
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
performance: {
|
|
467
|
+
mode: "auto",
|
|
468
|
+
maxDurationMs: DEFAULT_PERFORMANCE_MAX_DURATION_MS,
|
|
469
|
+
sampleIntervalMs: DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS,
|
|
470
|
+
longTaskThresholdMs: DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS,
|
|
471
|
+
saveDir: DEFAULT_PERFORMANCE_SAVE_DIR,
|
|
472
|
+
memory: {
|
|
473
|
+
enabled: true
|
|
474
|
+
},
|
|
475
|
+
stacks: {
|
|
476
|
+
enabled: true
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/performance/summary.ts
|
|
482
|
+
function buildPerformanceSummary(input) {
|
|
483
|
+
const memory = buildMemorySummary(input.memorySamples);
|
|
484
|
+
const blockedTimeMs = input.longTasks.reduce((total, task) => {
|
|
485
|
+
return total + Math.max(0, task.durationMs - DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS);
|
|
486
|
+
}, 0);
|
|
487
|
+
const longTaskCount = input.longTasks.length;
|
|
488
|
+
const durations = input.longTasks.map((task) => task.durationMs);
|
|
489
|
+
const maxTaskDurationMs = durations.length ? Math.max(...durations) : 0;
|
|
490
|
+
const averageTaskDurationMs = durations.length ? Math.round(
|
|
491
|
+
durations.reduce((total, duration) => total + duration, 0) / durations.length
|
|
492
|
+
) : void 0;
|
|
493
|
+
const suspectedJank = blockedTimeMs > 0 || memory.trend === "growing" || longTaskCount > 0;
|
|
494
|
+
const severity = resolveSeverity({
|
|
495
|
+
blockedTimeMs,
|
|
496
|
+
longTaskCount,
|
|
497
|
+
memoryTrend: memory.trend
|
|
498
|
+
});
|
|
499
|
+
return {
|
|
500
|
+
blockedTimeMs,
|
|
501
|
+
longTaskCount,
|
|
502
|
+
maxTaskDurationMs,
|
|
503
|
+
averageTaskDurationMs,
|
|
504
|
+
suspectedJank,
|
|
505
|
+
severity
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function buildMemorySummary(samples) {
|
|
509
|
+
const first = samples[0]?.usedJSHeapSize;
|
|
510
|
+
const last = samples.at(-1)?.usedJSHeapSize;
|
|
511
|
+
const peak = samples.reduce((currentPeak, sample) => {
|
|
512
|
+
if (typeof sample.usedJSHeapSize !== "number") {
|
|
513
|
+
return currentPeak;
|
|
514
|
+
}
|
|
515
|
+
return Math.max(currentPeak, sample.usedJSHeapSize);
|
|
516
|
+
}, 0);
|
|
517
|
+
const trend = resolveMemoryTrend(first, last);
|
|
518
|
+
return {
|
|
519
|
+
samples,
|
|
520
|
+
initialUsedJSHeapSize: first,
|
|
521
|
+
finalUsedJSHeapSize: last,
|
|
522
|
+
peakUsedJSHeapSize: peak || void 0,
|
|
523
|
+
deltaUsedJSHeapSize: typeof first === "number" && typeof last === "number" ? last - first : void 0,
|
|
524
|
+
trend
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function buildStackSummary(frames, options = {}) {
|
|
528
|
+
return {
|
|
529
|
+
topFrames: [...frames].sort(sortByHotness).slice(0, 10),
|
|
530
|
+
rawProfilePath: options.rawProfilePath,
|
|
531
|
+
limitation: options.limitation
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function resolveSeverity(input) {
|
|
535
|
+
if (input.blockedTimeMs >= 1e3 || input.longTaskCount >= 10) {
|
|
536
|
+
return "critical";
|
|
537
|
+
}
|
|
538
|
+
if (input.blockedTimeMs > 0 || input.memoryTrend === "growing") {
|
|
539
|
+
return "warning";
|
|
540
|
+
}
|
|
541
|
+
return "ok";
|
|
542
|
+
}
|
|
543
|
+
function resolveMemoryTrend(first, last) {
|
|
544
|
+
if (typeof first !== "number" || typeof last !== "number") {
|
|
545
|
+
return "unknown";
|
|
546
|
+
}
|
|
547
|
+
if (last > first) {
|
|
548
|
+
return "growing";
|
|
549
|
+
}
|
|
550
|
+
return "stable";
|
|
551
|
+
}
|
|
552
|
+
function sortByHotness(left, right) {
|
|
553
|
+
return compareNumber(right.totalTimeMs, left.totalTimeMs) || compareNumber(right.selfTimeMs, left.selfTimeMs) || compareNumber(right.hitCount, left.hitCount);
|
|
554
|
+
}
|
|
555
|
+
function compareNumber(left, right) {
|
|
556
|
+
return (left ?? -1) - (right ?? -1);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// src/runtime/performanceHook.ts
|
|
560
|
+
var activePerformanceCollector;
|
|
561
|
+
function installPerformanceHook(options) {
|
|
562
|
+
const collector = createPerformanceCollector({
|
|
563
|
+
pageId: options.pageId,
|
|
564
|
+
now: () => Date.now(),
|
|
565
|
+
readMemory: readBrowserMemory,
|
|
566
|
+
observeLongTask: (push) => observeLongTasks(push, options.longTaskThresholdMs),
|
|
567
|
+
observeAnimationFrame: observeAnimationFrameTasks,
|
|
568
|
+
observeErrorStack: observeWindowErrorStack,
|
|
569
|
+
observeUnhandledRejectionStack,
|
|
570
|
+
setTimeout: window.setTimeout.bind(window),
|
|
571
|
+
clearTimeout: window.clearTimeout.bind(window)
|
|
572
|
+
});
|
|
573
|
+
const decoratedCollector = options.send ? createDispatchingCollector(collector, options.send) : collector;
|
|
574
|
+
activePerformanceCollector = decoratedCollector;
|
|
575
|
+
return decoratedCollector;
|
|
576
|
+
}
|
|
577
|
+
function getPerformanceCollector() {
|
|
578
|
+
return activePerformanceCollector;
|
|
579
|
+
}
|
|
580
|
+
function createPerformanceCollector(deps) {
|
|
581
|
+
const state = {
|
|
582
|
+
longTasks: [],
|
|
583
|
+
stackFrames: [],
|
|
584
|
+
memorySamples: [],
|
|
585
|
+
latestReport: void 0,
|
|
586
|
+
activeRecordingId: void 0,
|
|
587
|
+
activeRecordingStartedAt: 0,
|
|
588
|
+
activeIncludeMemory: true,
|
|
589
|
+
activeIncludeStacks: true
|
|
590
|
+
};
|
|
591
|
+
const cleanups = [
|
|
592
|
+
deps.observeLongTask((task) => {
|
|
593
|
+
state.longTasks.push(task);
|
|
594
|
+
}),
|
|
595
|
+
deps.observeAnimationFrame((task) => {
|
|
596
|
+
state.longTasks.push(task);
|
|
597
|
+
})
|
|
598
|
+
];
|
|
599
|
+
if (deps.observeErrorStack) {
|
|
600
|
+
cleanups.push(
|
|
601
|
+
deps.observeErrorStack((frame) => {
|
|
602
|
+
state.stackFrames.push(frame);
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
if (deps.observeUnhandledRejectionStack) {
|
|
607
|
+
cleanups.push(
|
|
608
|
+
deps.observeUnhandledRejectionStack((frame) => {
|
|
609
|
+
state.stackFrames.push(frame);
|
|
610
|
+
})
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
async recordOnce(options) {
|
|
615
|
+
const recordingId = startSession(state, deps, {
|
|
616
|
+
includeMemory: options.includeMemory,
|
|
617
|
+
includeStacks: options.includeStacks
|
|
618
|
+
});
|
|
619
|
+
await waitForDuration(deps, options.durationMs);
|
|
620
|
+
return stopSession({
|
|
621
|
+
state,
|
|
622
|
+
deps,
|
|
623
|
+
recordingId,
|
|
624
|
+
source: "hook"
|
|
625
|
+
});
|
|
626
|
+
},
|
|
627
|
+
start(options) {
|
|
628
|
+
if (state.activeRecordingId) {
|
|
629
|
+
throw new Error("A performance recording is already active");
|
|
630
|
+
}
|
|
631
|
+
return startSession(state, deps, options);
|
|
632
|
+
},
|
|
633
|
+
stop(recordingId) {
|
|
634
|
+
return stopSession({
|
|
635
|
+
state,
|
|
636
|
+
deps,
|
|
637
|
+
recordingId,
|
|
638
|
+
source: "hook"
|
|
639
|
+
});
|
|
640
|
+
},
|
|
641
|
+
latest() {
|
|
642
|
+
return state.latestReport;
|
|
643
|
+
},
|
|
644
|
+
dispose() {
|
|
645
|
+
activePerformanceCollector = void 0;
|
|
646
|
+
for (const cleanup of cleanups) {
|
|
647
|
+
cleanup();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function createRecordingId() {
|
|
653
|
+
return `performance-${(0, import_nanoid4.nanoid)()}`;
|
|
654
|
+
}
|
|
655
|
+
function startSession(state, deps, options) {
|
|
656
|
+
const recordingId = createRecordingId();
|
|
657
|
+
state.longTasks.length = 0;
|
|
658
|
+
state.stackFrames.length = 0;
|
|
659
|
+
state.memorySamples.length = 0;
|
|
660
|
+
if (options.includeMemory) {
|
|
661
|
+
const sample = deps.readMemory();
|
|
662
|
+
if (sample) {
|
|
663
|
+
state.memorySamples.push(sample);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
state.activeRecordingId = recordingId;
|
|
667
|
+
state.activeRecordingStartedAt = deps.now();
|
|
668
|
+
state.activeIncludeMemory = options.includeMemory;
|
|
669
|
+
state.activeIncludeStacks = options.includeStacks;
|
|
670
|
+
return recordingId;
|
|
671
|
+
}
|
|
672
|
+
function createDispatchingCollector(collector, send) {
|
|
673
|
+
return {
|
|
674
|
+
async recordOnce(options) {
|
|
675
|
+
const report = await collector.recordOnce(options);
|
|
676
|
+
send(report);
|
|
677
|
+
return report;
|
|
678
|
+
},
|
|
679
|
+
start(options) {
|
|
680
|
+
return collector.start(options);
|
|
681
|
+
},
|
|
682
|
+
stop(recordingId) {
|
|
683
|
+
const report = collector.stop(recordingId);
|
|
684
|
+
send(report);
|
|
685
|
+
return report;
|
|
686
|
+
},
|
|
687
|
+
latest() {
|
|
688
|
+
return collector.latest();
|
|
689
|
+
},
|
|
690
|
+
dispose() {
|
|
691
|
+
collector.dispose();
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
function waitForDuration(deps, durationMs) {
|
|
696
|
+
return new Promise((resolve) => {
|
|
697
|
+
const timer = deps.setTimeout(() => {
|
|
698
|
+
deps.clearTimeout(timer);
|
|
699
|
+
resolve();
|
|
700
|
+
}, durationMs);
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
function buildReport(options) {
|
|
704
|
+
const memory = options.includeMemory ? buildMemorySummary(options.memorySamples) : void 0;
|
|
705
|
+
const summary = buildPerformanceSummary({
|
|
706
|
+
longTasks: options.longTasks,
|
|
707
|
+
memorySamples: options.includeMemory ? options.memorySamples : []
|
|
708
|
+
});
|
|
709
|
+
const stacks = options.includeStacks ? buildStackSummary(options.stackFrames, {
|
|
710
|
+
rawProfilePath: options.rawProfilePath,
|
|
711
|
+
limitation: options.stackFrames.length > 0 ? void 0 : "Runtime path only exposes error stacks when the page reports them"
|
|
712
|
+
}) : void 0;
|
|
713
|
+
const report = {
|
|
714
|
+
recordingId: options.recordingId,
|
|
715
|
+
pageId: options.pageId,
|
|
716
|
+
source: options.source,
|
|
717
|
+
startedAt: options.startedAt,
|
|
718
|
+
endedAt: options.endedAt,
|
|
719
|
+
durationMs: options.endedAt - options.startedAt,
|
|
720
|
+
summary,
|
|
721
|
+
longTasks: [...options.longTasks],
|
|
722
|
+
memory,
|
|
723
|
+
stacks,
|
|
724
|
+
artifacts: options.artifacts,
|
|
725
|
+
limitations: options.limitations
|
|
726
|
+
};
|
|
727
|
+
return report;
|
|
728
|
+
}
|
|
729
|
+
function stopSession(options) {
|
|
730
|
+
const { state, deps, recordingId, source } = options;
|
|
731
|
+
if (!state.activeRecordingId || state.activeRecordingId !== recordingId) {
|
|
732
|
+
throw new Error(`Performance recording not found: ${recordingId}`);
|
|
733
|
+
}
|
|
734
|
+
if (state.activeIncludeMemory) {
|
|
735
|
+
const sample = deps.readMemory();
|
|
736
|
+
if (sample) {
|
|
737
|
+
state.memorySamples.push(sample);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
const report = buildReport({
|
|
741
|
+
recordingId: state.activeRecordingId,
|
|
742
|
+
pageId: deps.pageId,
|
|
743
|
+
startedAt: state.activeRecordingStartedAt,
|
|
744
|
+
endedAt: deps.now(),
|
|
745
|
+
source,
|
|
746
|
+
includeMemory: state.activeIncludeMemory,
|
|
747
|
+
includeStacks: state.activeIncludeStacks,
|
|
748
|
+
longTasks: state.longTasks,
|
|
749
|
+
memorySamples: state.memorySamples,
|
|
750
|
+
stackFrames: state.stackFrames,
|
|
751
|
+
limitations: [
|
|
752
|
+
"Runtime path only sees browser-observable signals",
|
|
753
|
+
"Runtime path cannot produce a full CPU profile or heap snapshot"
|
|
754
|
+
]
|
|
755
|
+
});
|
|
756
|
+
state.latestReport = report;
|
|
757
|
+
state.activeRecordingId = void 0;
|
|
758
|
+
state.activeRecordingStartedAt = 0;
|
|
759
|
+
state.activeIncludeMemory = true;
|
|
760
|
+
state.activeIncludeStacks = true;
|
|
761
|
+
return report;
|
|
762
|
+
}
|
|
763
|
+
function readBrowserMemory() {
|
|
764
|
+
const memory = performance.memory;
|
|
765
|
+
if (!memory) {
|
|
766
|
+
return void 0;
|
|
767
|
+
}
|
|
768
|
+
return {
|
|
769
|
+
timestamp: Date.now(),
|
|
770
|
+
usedJSHeapSize: memory.usedJSHeapSize,
|
|
771
|
+
totalJSHeapSize: memory.totalJSHeapSize,
|
|
772
|
+
jsHeapSizeLimit: memory.jsHeapSizeLimit
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function observeLongTasks(push, longTaskThresholdMs = 50) {
|
|
776
|
+
if (typeof PerformanceObserver === "undefined") {
|
|
777
|
+
return () => {
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
const observer = new PerformanceObserver((list) => {
|
|
781
|
+
for (const entry of list.getEntries()) {
|
|
782
|
+
if (entry.duration < longTaskThresholdMs) {
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
push({
|
|
786
|
+
startTime: entry.startTime,
|
|
787
|
+
durationMs: entry.duration,
|
|
788
|
+
name: entry.name,
|
|
789
|
+
source: "longtask"
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
observer.observe({ type: "longtask", buffered: true });
|
|
794
|
+
return () => {
|
|
795
|
+
observer.disconnect();
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function observeAnimationFrameTasks(push) {
|
|
799
|
+
if (typeof PerformanceObserver === "undefined") {
|
|
800
|
+
return () => {
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
const supported = PerformanceObserver.supportedEntryTypes.includes(
|
|
804
|
+
"long-animation-frame"
|
|
805
|
+
);
|
|
806
|
+
if (!supported) {
|
|
807
|
+
return () => {
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
const observer = new PerformanceObserver((list) => {
|
|
811
|
+
for (const entry of list.getEntries()) {
|
|
812
|
+
push({
|
|
813
|
+
startTime: entry.startTime,
|
|
814
|
+
durationMs: entry.duration,
|
|
815
|
+
name: entry.name,
|
|
816
|
+
source: "long-animation-frame"
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
observer.observe({ type: "long-animation-frame", buffered: true });
|
|
821
|
+
return () => {
|
|
822
|
+
observer.disconnect();
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
function observeWindowErrorStack(push) {
|
|
826
|
+
const onError = (event) => {
|
|
827
|
+
const error = event.error;
|
|
828
|
+
const frames = parseStackFrames(error?.stack);
|
|
829
|
+
if (frames.length === 0 && event.message) {
|
|
830
|
+
push({ functionName: event.message });
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
frames.forEach((frame) => {
|
|
834
|
+
push(frame);
|
|
835
|
+
});
|
|
836
|
+
};
|
|
837
|
+
window.addEventListener("error", onError);
|
|
838
|
+
return () => {
|
|
839
|
+
window.removeEventListener("error", onError);
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
function observeUnhandledRejectionStack(push) {
|
|
843
|
+
const onRejection = (event) => {
|
|
844
|
+
const reason = event.reason;
|
|
845
|
+
const frames = parseStackFrames(reason?.stack);
|
|
846
|
+
if (frames.length === 0 && reason?.message) {
|
|
847
|
+
push({ functionName: reason.message });
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
frames.forEach((frame) => {
|
|
851
|
+
push(frame);
|
|
852
|
+
});
|
|
853
|
+
};
|
|
854
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
855
|
+
return () => {
|
|
856
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function parseStackFrames(stack) {
|
|
860
|
+
if (!stack) {
|
|
861
|
+
return [];
|
|
862
|
+
}
|
|
863
|
+
return stack.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
864
|
+
const match = /at\s+(.*?)\s+\((.*?):(\d+):(\d+)\)$/.exec(line);
|
|
865
|
+
if (match) {
|
|
866
|
+
return {
|
|
867
|
+
functionName: match[1] || "<anonymous>",
|
|
868
|
+
url: match[2],
|
|
869
|
+
lineNumber: Number(match[3]),
|
|
870
|
+
columnNumber: Number(match[4])
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
return { functionName: line };
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
|
|
330
877
|
// src/runtime/screenshot.ts
|
|
331
878
|
var snapdomLoader = () => Promise.reject(createMissingSnapdomError());
|
|
332
879
|
var screenshotModuleRegistry = {};
|
|
@@ -736,7 +1283,79 @@ function createClientVueRuntimeRpc(getRpc) {
|
|
|
736
1283
|
});
|
|
737
1284
|
getRpc().onPiniaInfoUpdated(query.event, (0, import_devtools_kit.stringify)(result));
|
|
738
1285
|
},
|
|
739
|
-
onPiniaInfoUpdated: () => void 0
|
|
1286
|
+
onPiniaInfoUpdated: () => void 0,
|
|
1287
|
+
async recordPerformance(query) {
|
|
1288
|
+
const collector = getPerformanceCollector();
|
|
1289
|
+
if (!collector) {
|
|
1290
|
+
getRpc().onPerformanceRecorded(
|
|
1291
|
+
query.event,
|
|
1292
|
+
createPerformanceUnavailableError()
|
|
1293
|
+
);
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
try {
|
|
1297
|
+
const report = await collector.recordOnce({
|
|
1298
|
+
durationMs: query.durationMs,
|
|
1299
|
+
includeMemory: query.includeMemory,
|
|
1300
|
+
includeStacks: query.includeStacks
|
|
1301
|
+
});
|
|
1302
|
+
getRpc().onPerformanceRecorded(query.event, report);
|
|
1303
|
+
} catch (error) {
|
|
1304
|
+
getRpc().onPerformanceRecorded(
|
|
1305
|
+
query.event,
|
|
1306
|
+
createPerformanceError(error)
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
onPerformanceRecorded: () => void 0,
|
|
1311
|
+
startPerformanceRecording(query) {
|
|
1312
|
+
const collector = getPerformanceCollector();
|
|
1313
|
+
if (!collector) {
|
|
1314
|
+
getRpc().onPerformanceRecordingStarted(
|
|
1315
|
+
query.event,
|
|
1316
|
+
createPerformanceUnavailableError()
|
|
1317
|
+
);
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
try {
|
|
1321
|
+
const recordingId = collector.start({
|
|
1322
|
+
includeMemory: query.includeMemory,
|
|
1323
|
+
includeStacks: query.includeStacks
|
|
1324
|
+
});
|
|
1325
|
+
getRpc().onPerformanceRecordingStarted(query.event, {
|
|
1326
|
+
ok: true,
|
|
1327
|
+
recordingId,
|
|
1328
|
+
startedAt: Date.now(),
|
|
1329
|
+
source: "hook"
|
|
1330
|
+
});
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
getRpc().onPerformanceRecordingStarted(
|
|
1333
|
+
query.event,
|
|
1334
|
+
createPerformanceError(error)
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
onPerformanceRecordingStarted: () => void 0,
|
|
1339
|
+
stopPerformanceRecording(query) {
|
|
1340
|
+
const collector = getPerformanceCollector();
|
|
1341
|
+
if (!collector) {
|
|
1342
|
+
getRpc().onPerformanceRecordingStopped(
|
|
1343
|
+
query.event,
|
|
1344
|
+
createPerformanceUnavailableError()
|
|
1345
|
+
);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
try {
|
|
1349
|
+
const report = collector.stop(query.recordingId);
|
|
1350
|
+
getRpc().onPerformanceRecordingStopped(query.event, report);
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
getRpc().onPerformanceRecordingStopped(
|
|
1353
|
+
query.event,
|
|
1354
|
+
createPerformanceError(error)
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
},
|
|
1358
|
+
onPerformanceRecordingStopped: () => void 0
|
|
740
1359
|
};
|
|
741
1360
|
}
|
|
742
1361
|
function setStateValue(object, path, value) {
|
|
@@ -771,6 +1390,18 @@ function callVueDevtoolsHook(name, payload) {
|
|
|
771
1390
|
const hooks = import_devtools_kit.devtools.ctx.hooks;
|
|
772
1391
|
hooks.callHook(name, payload);
|
|
773
1392
|
}
|
|
1393
|
+
function createPerformanceUnavailableError() {
|
|
1394
|
+
return {
|
|
1395
|
+
ok: false,
|
|
1396
|
+
error: "Performance collector is not initialized"
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
function createPerformanceError(error) {
|
|
1400
|
+
return {
|
|
1401
|
+
ok: false,
|
|
1402
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
774
1405
|
async function findComponentNode(componentName) {
|
|
775
1406
|
const inspectorTree = await import_devtools_kit.devtools.api.getInspectorTree({
|
|
776
1407
|
inspectorId: COMPONENTS_INSPECTOR_ID,
|
|
@@ -829,11 +1460,18 @@ async function startRuntimeClient() {
|
|
|
829
1460
|
const identity = getRuntimePageIdentity({
|
|
830
1461
|
href: window.location.href,
|
|
831
1462
|
title: document.title,
|
|
1463
|
+
runtimeClientId: getRuntimeClientId(window.sessionStorage, window),
|
|
832
1464
|
innerWidth: window.innerWidth,
|
|
833
1465
|
innerHeight: window.innerHeight,
|
|
834
1466
|
readyState: document.readyState
|
|
835
1467
|
});
|
|
836
1468
|
hot.send("vite-plugin-vue-mcp-next:page-connected", identity);
|
|
1469
|
+
installPerformanceHook({
|
|
1470
|
+
pageId: identity.pageId,
|
|
1471
|
+
send(report) {
|
|
1472
|
+
hot.send("vite-plugin-vue-mcp-next:performance-record", report);
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
837
1475
|
installConsoleHook({
|
|
838
1476
|
pageId: identity.pageId,
|
|
839
1477
|
send(record) {
|