@tontoko/fast-playwright-mcp 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +1047 -0
- package/cli.js +18 -0
- package/config.d.ts +124 -0
- package/index.d.ts +25 -0
- package/index.js +18 -0
- package/lib/actions.d.js +0 -0
- package/lib/batch/batch-executor.js +137 -0
- package/lib/browser-context-factory.js +252 -0
- package/lib/browser-server-backend.js +139 -0
- package/lib/config/constants.js +80 -0
- package/lib/config.js +405 -0
- package/lib/context.js +274 -0
- package/lib/diagnostics/common/diagnostic-base.js +63 -0
- package/lib/diagnostics/common/error-enrichment-utils.js +212 -0
- package/lib/diagnostics/common/index.js +56 -0
- package/lib/diagnostics/common/initialization-manager.js +210 -0
- package/lib/diagnostics/common/performance-tracker.js +132 -0
- package/lib/diagnostics/diagnostic-error.js +140 -0
- package/lib/diagnostics/diagnostic-level.js +123 -0
- package/lib/diagnostics/diagnostic-thresholds.js +347 -0
- package/lib/diagnostics/element-discovery.js +441 -0
- package/lib/diagnostics/enhanced-error-handler.js +376 -0
- package/lib/diagnostics/error-enrichment.js +157 -0
- package/lib/diagnostics/frame-reference-manager.js +179 -0
- package/lib/diagnostics/page-analyzer.js +639 -0
- package/lib/diagnostics/parallel-page-analyzer.js +129 -0
- package/lib/diagnostics/resource-manager.js +134 -0
- package/lib/diagnostics/smart-config.js +482 -0
- package/lib/diagnostics/smart-handle.js +118 -0
- package/lib/diagnostics/unified-system.js +717 -0
- package/lib/extension/cdp-relay.js +486 -0
- package/lib/extension/extension-context-factory.js +74 -0
- package/lib/extension/main.js +41 -0
- package/lib/file-utils.js +42 -0
- package/lib/generate-keys.js +75 -0
- package/lib/http-server.js +50 -0
- package/lib/in-process-client.js +64 -0
- package/lib/index.js +48 -0
- package/lib/javascript.js +90 -0
- package/lib/log.js +33 -0
- package/lib/loop/loop-claude.js +247 -0
- package/lib/loop/loop-open-ai.js +222 -0
- package/lib/loop/loop.js +174 -0
- package/lib/loop/main.js +46 -0
- package/lib/loopTools/context.js +76 -0
- package/lib/loopTools/main.js +65 -0
- package/lib/loopTools/perform.js +40 -0
- package/lib/loopTools/snapshot.js +37 -0
- package/lib/loopTools/tool.js +26 -0
- package/lib/manual-promise.js +125 -0
- package/lib/mcp/in-process-transport.js +91 -0
- package/lib/mcp/proxy-backend.js +127 -0
- package/lib/mcp/server.js +123 -0
- package/lib/mcp/transport.js +159 -0
- package/lib/package.js +28 -0
- package/lib/program.js +82 -0
- package/lib/response.js +493 -0
- package/lib/schemas/expectation.js +152 -0
- package/lib/session-log.js +210 -0
- package/lib/tab.js +417 -0
- package/lib/tools/base-tool-handler.js +141 -0
- package/lib/tools/batch-execute.js +150 -0
- package/lib/tools/common.js +65 -0
- package/lib/tools/console.js +60 -0
- package/lib/tools/diagnose/diagnose-analysis-runner.js +101 -0
- package/lib/tools/diagnose/diagnose-config-handler.js +130 -0
- package/lib/tools/diagnose/diagnose-report-builder.js +394 -0
- package/lib/tools/diagnose.js +147 -0
- package/lib/tools/dialogs.js +57 -0
- package/lib/tools/evaluate.js +67 -0
- package/lib/tools/files.js +53 -0
- package/lib/tools/find-elements.js +307 -0
- package/lib/tools/install.js +60 -0
- package/lib/tools/keyboard.js +93 -0
- package/lib/tools/mouse.js +110 -0
- package/lib/tools/navigate.js +82 -0
- package/lib/tools/network.js +50 -0
- package/lib/tools/pdf.js +46 -0
- package/lib/tools/screenshot.js +113 -0
- package/lib/tools/snapshot.js +158 -0
- package/lib/tools/tabs.js +97 -0
- package/lib/tools/tool.js +47 -0
- package/lib/tools/utils.js +131 -0
- package/lib/tools/wait.js +64 -0
- package/lib/tools.js +65 -0
- package/lib/types/batch.js +47 -0
- package/lib/types/diff.js +0 -0
- package/lib/types/performance.js +0 -0
- package/lib/types/threshold-base.js +0 -0
- package/lib/utils/array-utils.js +44 -0
- package/lib/utils/code-deduplication-utils.js +141 -0
- package/lib/utils/common-formatters.js +252 -0
- package/lib/utils/console-filter.js +64 -0
- package/lib/utils/diagnostic-report-utils.js +178 -0
- package/lib/utils/diff-formatter.js +126 -0
- package/lib/utils/disposable-manager.js +135 -0
- package/lib/utils/error-handler-middleware.js +77 -0
- package/lib/utils/image-processor.js +137 -0
- package/lib/utils/index.js +92 -0
- package/lib/utils/report-builder.js +189 -0
- package/lib/utils/request-logger.js +82 -0
- package/lib/utils/response-diff-detector.js +150 -0
- package/lib/utils/section-builder.js +62 -0
- package/lib/utils/tool-patterns.js +153 -0
- package/lib/utils.js +46 -0
- package/package.json +77 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/common-formatters.ts
|
|
21
|
+
import debug from "debug";
|
|
22
|
+
var formattersDebug = debug("pw:mcp:formatters");
|
|
23
|
+
function formatPerformanceMetric(name, value, unit, threshold) {
|
|
24
|
+
const icon = threshold && value > threshold ? "⚠️" : "✅";
|
|
25
|
+
const thresholdText = threshold ? ` (threshold: ${threshold}${unit})` : "";
|
|
26
|
+
return `${icon} **${name}**: ${value}${unit}${thresholdText}`;
|
|
27
|
+
}
|
|
28
|
+
function formatConfidence(confidence) {
|
|
29
|
+
return `${Math.round(confidence * 100)}%`;
|
|
30
|
+
}
|
|
31
|
+
function formatExecutionTime(ms) {
|
|
32
|
+
if (ms < 1000) {
|
|
33
|
+
return `${Math.round(ms)}ms`;
|
|
34
|
+
}
|
|
35
|
+
if (ms < 60000) {
|
|
36
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
37
|
+
}
|
|
38
|
+
const minutes = Math.floor(ms / 60000);
|
|
39
|
+
const seconds = Math.round(ms % 60000 / 1000);
|
|
40
|
+
return `${minutes}m ${seconds}s`;
|
|
41
|
+
}
|
|
42
|
+
function getPerformanceIcon(input) {
|
|
43
|
+
if ("significance" in input) {
|
|
44
|
+
switch (input.significance) {
|
|
45
|
+
case "significant":
|
|
46
|
+
return "\uD83D\uDD34";
|
|
47
|
+
case "notable":
|
|
48
|
+
return "\uD83D\uDFE1";
|
|
49
|
+
default:
|
|
50
|
+
return "\uD83D\uDFE2";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const { value, thresholds } = input;
|
|
54
|
+
if (value <= thresholds.good) {
|
|
55
|
+
return "\uD83D\uDFE2";
|
|
56
|
+
}
|
|
57
|
+
if (value <= thresholds.warning) {
|
|
58
|
+
return "\uD83D\uDFE1";
|
|
59
|
+
}
|
|
60
|
+
return "\uD83D\uDD34";
|
|
61
|
+
}
|
|
62
|
+
function getImpactIcon(impact) {
|
|
63
|
+
switch (impact) {
|
|
64
|
+
case "high":
|
|
65
|
+
return "\uD83D\uDD34";
|
|
66
|
+
case "medium":
|
|
67
|
+
return "\uD83D\uDFE1";
|
|
68
|
+
case "low":
|
|
69
|
+
return "\uD83D\uDFE2";
|
|
70
|
+
default:
|
|
71
|
+
return "⚪";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function getRecommendationIcon(type) {
|
|
75
|
+
switch (type) {
|
|
76
|
+
case "warning":
|
|
77
|
+
return "⚠️";
|
|
78
|
+
case "optimization":
|
|
79
|
+
return "⚡";
|
|
80
|
+
default:
|
|
81
|
+
return "ℹ️";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function formatDiagnosticPair(key, value) {
|
|
85
|
+
let formattedValue;
|
|
86
|
+
if (typeof value === "boolean") {
|
|
87
|
+
formattedValue = value ? "Yes" : "No";
|
|
88
|
+
} else {
|
|
89
|
+
formattedValue = value.toString();
|
|
90
|
+
}
|
|
91
|
+
return `- **${key}:** ${formattedValue}`;
|
|
92
|
+
}
|
|
93
|
+
function buildSection(title, content, level = 2, options = {}) {
|
|
94
|
+
const { emptyLineAfter = false, emptyLineBefore = true } = options;
|
|
95
|
+
const prefix = "#".repeat(level);
|
|
96
|
+
const result = [];
|
|
97
|
+
if (emptyLineBefore) {
|
|
98
|
+
result.push("");
|
|
99
|
+
}
|
|
100
|
+
result.push(`${prefix} ${title}`, ...content);
|
|
101
|
+
if (emptyLineAfter) {
|
|
102
|
+
result.push("");
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
function addConditional(array, condition, items) {
|
|
107
|
+
if (condition) {
|
|
108
|
+
if (Array.isArray(items)) {
|
|
109
|
+
array.push(...items);
|
|
110
|
+
} else {
|
|
111
|
+
array.push(items);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function generateMouseMoveCode(x, y) {
|
|
116
|
+
return `await page.mouse.move(${x}, ${y});`;
|
|
117
|
+
}
|
|
118
|
+
function generateMouseClickCode() {
|
|
119
|
+
return ["await page.mouse.down();", "await page.mouse.up();"];
|
|
120
|
+
}
|
|
121
|
+
function generateMouseDragCode(startX, startY, endX, endY) {
|
|
122
|
+
return [
|
|
123
|
+
`// Drag mouse from (${startX}, ${startY}) to (${endX}, ${endY})`,
|
|
124
|
+
generateMouseMoveCode(startX, startY),
|
|
125
|
+
"await page.mouse.down();",
|
|
126
|
+
generateMouseMoveCode(endX, endY),
|
|
127
|
+
"await page.mouse.up();"
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
function generateNavigationCode(url) {
|
|
131
|
+
return `await page.goto('${url}');`;
|
|
132
|
+
}
|
|
133
|
+
function generateBackCode() {
|
|
134
|
+
return "await page.goBack();";
|
|
135
|
+
}
|
|
136
|
+
function generateForwardCode() {
|
|
137
|
+
return "await page.goForward();";
|
|
138
|
+
}
|
|
139
|
+
function generateKeyPressCode(key) {
|
|
140
|
+
return `await page.keyboard.press('${key}');`;
|
|
141
|
+
}
|
|
142
|
+
function generateEvaluationCode(functionCode) {
|
|
143
|
+
return `await page.evaluate(${quote(functionCode)});`;
|
|
144
|
+
}
|
|
145
|
+
function generateLocatorEvaluationCode(locator, functionCode) {
|
|
146
|
+
return `await page.${locator}.evaluate(${quote(functionCode)});`;
|
|
147
|
+
}
|
|
148
|
+
function quote(str) {
|
|
149
|
+
return `'${str.replace(/'/g, "\\'")}'`;
|
|
150
|
+
}
|
|
151
|
+
function getErrorMessage(error, fallback = "Unknown error") {
|
|
152
|
+
if (error instanceof Error) {
|
|
153
|
+
return error.message;
|
|
154
|
+
}
|
|
155
|
+
return fallback;
|
|
156
|
+
}
|
|
157
|
+
function createDiagnosticErrorInfo(error, operation = "Unknown operation", component = "Unknown component") {
|
|
158
|
+
return {
|
|
159
|
+
error: getErrorMessage(error),
|
|
160
|
+
operation,
|
|
161
|
+
component
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function handleResourceDisposalError(error, resourceType, logger = formattersDebug) {
|
|
165
|
+
logger(`${resourceType} disposal failed: ${getErrorMessage(error)}`);
|
|
166
|
+
}
|
|
167
|
+
function handleFrameAccessError(error, frameInfo) {
|
|
168
|
+
return {
|
|
169
|
+
reason: getErrorMessage(error, "Access denied"),
|
|
170
|
+
...frameInfo && { frameInfo }
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function formatStatusString(text, status, iconType, additionalText) {
|
|
174
|
+
let icon;
|
|
175
|
+
switch (iconType) {
|
|
176
|
+
case "performance":
|
|
177
|
+
if (["significant", "notable", "minimal", "normal"].includes(status)) {
|
|
178
|
+
icon = getPerformanceIcon({
|
|
179
|
+
significance: status
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
icon = getPerformanceIcon({ significance: "normal" });
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
case "impact":
|
|
186
|
+
icon = getImpactIcon(status);
|
|
187
|
+
break;
|
|
188
|
+
case "recommendation":
|
|
189
|
+
icon = getRecommendationIcon(status);
|
|
190
|
+
break;
|
|
191
|
+
default:
|
|
192
|
+
icon = "⚪";
|
|
193
|
+
}
|
|
194
|
+
const formattedText = additionalText ? `${text} ${additionalText}` : text;
|
|
195
|
+
return ` ${icon} ${formattedText}`;
|
|
196
|
+
}
|
|
197
|
+
function formatPerformanceComparison(component, expected, actual, deviation) {
|
|
198
|
+
const significance = deviation?.significance ?? "normal";
|
|
199
|
+
const icon = getPerformanceIcon({
|
|
200
|
+
significance
|
|
201
|
+
});
|
|
202
|
+
let deviationText = "";
|
|
203
|
+
if (deviation) {
|
|
204
|
+
const sign = deviation.percent > 0 ? "+" : "";
|
|
205
|
+
deviationText = ` (${sign}${deviation.percent}% ${deviation.significance})`;
|
|
206
|
+
}
|
|
207
|
+
return ` ${icon} **${component}**: Expected ${expected}ms, Actual ${actual.toFixed(0)}ms${deviationText}`;
|
|
208
|
+
}
|
|
209
|
+
function formatMetricName(key, nameMap) {
|
|
210
|
+
const defaultNameMap = {
|
|
211
|
+
domContentLoaded: "DOM Content Loaded",
|
|
212
|
+
loadComplete: "Load Complete",
|
|
213
|
+
firstPaint: "First Paint",
|
|
214
|
+
firstContentfulPaint: "First Contentful Paint"
|
|
215
|
+
};
|
|
216
|
+
const combinedMap = { ...defaultNameMap, ...nameMap };
|
|
217
|
+
return combinedMap[key] || key;
|
|
218
|
+
}
|
|
219
|
+
function processBrowserMetrics(browserMetrics, nameMap) {
|
|
220
|
+
return Object.entries(browserMetrics).filter(([_, value]) => typeof value === "number" && value > 0).map(([key, value]) => [
|
|
221
|
+
formatMetricName(key, nameMap),
|
|
222
|
+
`${value.toFixed(2)}ms`
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
export {
|
|
226
|
+
processBrowserMetrics,
|
|
227
|
+
handleResourceDisposalError,
|
|
228
|
+
handleFrameAccessError,
|
|
229
|
+
getRecommendationIcon,
|
|
230
|
+
getPerformanceIcon,
|
|
231
|
+
getImpactIcon,
|
|
232
|
+
getErrorMessage,
|
|
233
|
+
generateNavigationCode,
|
|
234
|
+
generateMouseMoveCode,
|
|
235
|
+
generateMouseDragCode,
|
|
236
|
+
generateMouseClickCode,
|
|
237
|
+
generateLocatorEvaluationCode,
|
|
238
|
+
generateKeyPressCode,
|
|
239
|
+
generateForwardCode,
|
|
240
|
+
generateEvaluationCode,
|
|
241
|
+
generateBackCode,
|
|
242
|
+
formatStatusString,
|
|
243
|
+
formatPerformanceMetric,
|
|
244
|
+
formatPerformanceComparison,
|
|
245
|
+
formatMetricName,
|
|
246
|
+
formatExecutionTime,
|
|
247
|
+
formatDiagnosticPair,
|
|
248
|
+
formatConfidence,
|
|
249
|
+
createDiagnosticErrorInfo,
|
|
250
|
+
buildSection,
|
|
251
|
+
addConditional
|
|
252
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/console-filter.ts
|
|
21
|
+
function filterConsoleMessages(messages, options) {
|
|
22
|
+
if (!options) {
|
|
23
|
+
return messages;
|
|
24
|
+
}
|
|
25
|
+
let filtered = messages;
|
|
26
|
+
if (options.levels && options.levels.length > 0) {
|
|
27
|
+
filtered = filtered.filter((msg) => {
|
|
28
|
+
const level = msg.type ?? "log";
|
|
29
|
+
return options.levels?.includes(level);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (options.patterns && options.patterns.length > 0) {
|
|
33
|
+
filtered = filtered.filter((msg) => {
|
|
34
|
+
const text = msg.toString();
|
|
35
|
+
return options.patterns?.some((pattern) => {
|
|
36
|
+
try {
|
|
37
|
+
const regex = new RegExp(pattern, "i");
|
|
38
|
+
return regex.test(text);
|
|
39
|
+
} catch {
|
|
40
|
+
return text.includes(pattern);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (options.removeDuplicates) {
|
|
46
|
+
const seen = new Set;
|
|
47
|
+
filtered = filtered.filter((msg) => {
|
|
48
|
+
const key = `${msg.type ?? "log"}:${msg.toString()}`;
|
|
49
|
+
if (seen.has(key)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
seen.add(key);
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const maxMessages = options.maxMessages ?? 10;
|
|
57
|
+
if (filtered.length > maxMessages) {
|
|
58
|
+
filtered = filtered.slice(-maxMessages);
|
|
59
|
+
}
|
|
60
|
+
return filtered;
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
filterConsoleMessages
|
|
64
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/diagnostic-report-utils.ts
|
|
21
|
+
import { getErrorMessage } from "./common-formatters.js";
|
|
22
|
+
function addKeyValuePairs(builder, configs) {
|
|
23
|
+
let currentBuilder = builder;
|
|
24
|
+
for (const config of configs) {
|
|
25
|
+
const formattedValue = config.formatter ? config.formatter(config.value) : formatValue(config.value);
|
|
26
|
+
currentBuilder = currentBuilder.addKeyValue(config.key, formattedValue);
|
|
27
|
+
}
|
|
28
|
+
return currentBuilder;
|
|
29
|
+
}
|
|
30
|
+
function addSystemHealthSection(builder, stats, sectionTitle = "Unified System Health") {
|
|
31
|
+
builder.addSection(sectionTitle, (sectionBuilder) => {
|
|
32
|
+
addKeyValuePairs(sectionBuilder, [
|
|
33
|
+
{ key: "System Status", value: stats.status },
|
|
34
|
+
{ key: "Total Operations", value: stats.totalOperations },
|
|
35
|
+
{
|
|
36
|
+
key: "Success Rate",
|
|
37
|
+
value: stats.successRate,
|
|
38
|
+
formatter: (value) => `${(value * 100).toFixed(1)}%`
|
|
39
|
+
},
|
|
40
|
+
{ key: "Active Handles", value: stats.activeHandles },
|
|
41
|
+
{ key: "Total Errors", value: stats.totalErrors }
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function addBasicPageStructure(builder, config) {
|
|
46
|
+
let currentBuilder = builder.addKeyValue("URL", config.url);
|
|
47
|
+
if (config.title) {
|
|
48
|
+
currentBuilder = currentBuilder.addKeyValue("Title", config.title);
|
|
49
|
+
}
|
|
50
|
+
return currentBuilder.addEmptyLine();
|
|
51
|
+
}
|
|
52
|
+
function addComprehensivePageStructure(builder, config) {
|
|
53
|
+
return addKeyValuePairs(builder, [
|
|
54
|
+
{ key: "URL", value: config.url },
|
|
55
|
+
{ key: "Title", value: config.title ?? "N/A" },
|
|
56
|
+
{
|
|
57
|
+
key: "IFrames",
|
|
58
|
+
value: `${config.iframes.count} iframes detected: ${config.iframes.detected}`
|
|
59
|
+
},
|
|
60
|
+
{ key: "Accessible iframes", value: config.iframes.accessible },
|
|
61
|
+
{ key: "Inaccessible iframes", value: config.iframes.inaccessible },
|
|
62
|
+
{ key: "Total visible elements", value: config.elements.totalVisible },
|
|
63
|
+
{
|
|
64
|
+
key: "Total interactable elements",
|
|
65
|
+
value: config.elements.totalInteractable
|
|
66
|
+
},
|
|
67
|
+
{ key: "Elements missing ARIA", value: config.elements.missingAria }
|
|
68
|
+
]).addEmptyLine();
|
|
69
|
+
}
|
|
70
|
+
function addDomComplexityMetrics(builder, metrics) {
|
|
71
|
+
if (!metrics.domMetrics) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const domMetrics = metrics.domMetrics;
|
|
75
|
+
builder.addEmptyLine().addHeader("DOM Complexity", 3);
|
|
76
|
+
addKeyValuePairs(builder, [
|
|
77
|
+
{ key: "Total DOM elements", value: domMetrics.totalElements ?? 0 },
|
|
78
|
+
{
|
|
79
|
+
key: "Max DOM depth",
|
|
80
|
+
value: domMetrics.maxDepth ?? 0,
|
|
81
|
+
formatter: (value) => `${value} levels`
|
|
82
|
+
}
|
|
83
|
+
]);
|
|
84
|
+
if (domMetrics.largeSubtrees?.length) {
|
|
85
|
+
builder.addKeyValue("Large subtrees detected", domMetrics.largeSubtrees.length);
|
|
86
|
+
for (const [index, subtree] of domMetrics.largeSubtrees.slice(0, 3).entries()) {
|
|
87
|
+
builder.addLine(` ${index + 1}. **${subtree.selector}**: ${subtree.elementCount} elements (${subtree.description})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function addInteractionMetrics(builder, metrics) {
|
|
92
|
+
if (!metrics.interactionMetrics) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
builder.addEmptyLine().addHeader("Interaction Elements", 3);
|
|
96
|
+
addKeyValuePairs(builder, [
|
|
97
|
+
{
|
|
98
|
+
key: "Clickable elements",
|
|
99
|
+
value: metrics.interactionMetrics.clickableElements ?? 0
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: "Form elements",
|
|
103
|
+
value: metrics.interactionMetrics.formElements ?? 0
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
key: "Disabled elements",
|
|
107
|
+
value: metrics.interactionMetrics.disabledElements ?? 0
|
|
108
|
+
}
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
function addResourceMetrics(builder, metrics) {
|
|
112
|
+
if (!metrics.resourceMetrics) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const resourceMetrics = metrics.resourceMetrics;
|
|
116
|
+
builder.addEmptyLine().addHeader("Resource Load", 3);
|
|
117
|
+
addKeyValuePairs(builder, [
|
|
118
|
+
{
|
|
119
|
+
key: "Images",
|
|
120
|
+
value: `${resourceMetrics.imageCount ?? 0} (${resourceMetrics.estimatedImageSize ?? "Unknown"}))`
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
key: "Script tags",
|
|
124
|
+
value: `${resourceMetrics.scriptTags ?? 0} (${resourceMetrics.externalScripts ?? 0} external, ${resourceMetrics.inlineScripts ?? 0} inline)`
|
|
125
|
+
},
|
|
126
|
+
{ key: "Stylesheets", value: resourceMetrics.stylesheetCount ?? 0 }
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
function addModalStatesIfPresent(builder, modalStates) {
|
|
130
|
+
if (modalStates.blockedBy.length > 0) {
|
|
131
|
+
builder.addHeader("Modal States", 2).addKeyValue("Active modals", modalStates.blockedBy.join(", ")).addEmptyLine();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function addErrorSection(builder, error, context) {
|
|
135
|
+
builder.addEmptyLine().addKeyValue(`Error ${context}`, getErrorMessage(error));
|
|
136
|
+
}
|
|
137
|
+
function formatValue(value) {
|
|
138
|
+
if (value === null || value === undefined) {
|
|
139
|
+
return "N/A";
|
|
140
|
+
}
|
|
141
|
+
if (typeof value === "boolean") {
|
|
142
|
+
return value.toString();
|
|
143
|
+
}
|
|
144
|
+
return String(value);
|
|
145
|
+
}
|
|
146
|
+
function addElementList(builder, title, elements, formatter, maxItems = 5) {
|
|
147
|
+
if (!elements?.length) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
builder.addEmptyLine().addLine(`**${title}:**`);
|
|
151
|
+
const limitedElements = elements.slice(0, maxItems);
|
|
152
|
+
for (let index = 0;index < limitedElements.length; index++) {
|
|
153
|
+
builder.addLine(formatter(limitedElements[index], index));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function addOptionalListSection(builder, title, items, formatter, headerLevel = 2) {
|
|
157
|
+
if (!items?.length) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
builder.addSection(title, (sectionBuilder) => {
|
|
161
|
+
for (const item of items) {
|
|
162
|
+
sectionBuilder.addListItem(formatter(item));
|
|
163
|
+
}
|
|
164
|
+
}, headerLevel);
|
|
165
|
+
}
|
|
166
|
+
export {
|
|
167
|
+
addSystemHealthSection,
|
|
168
|
+
addResourceMetrics,
|
|
169
|
+
addOptionalListSection,
|
|
170
|
+
addModalStatesIfPresent,
|
|
171
|
+
addKeyValuePairs,
|
|
172
|
+
addInteractionMetrics,
|
|
173
|
+
addErrorSection,
|
|
174
|
+
addElementList,
|
|
175
|
+
addDomComplexityMetrics,
|
|
176
|
+
addComprehensivePageStructure,
|
|
177
|
+
addBasicPageStructure
|
|
178
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
20
|
+
// src/utils/diff-formatter.ts
|
|
21
|
+
class DiffFormatter {
|
|
22
|
+
formatUnified(segments, context = 3) {
|
|
23
|
+
if (segments.length === 0) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
const lines = [];
|
|
27
|
+
let hasChanges = false;
|
|
28
|
+
for (const segment of segments) {
|
|
29
|
+
const processedSegment = this.processUnifiedSegment(segment, lines, hasChanges, context);
|
|
30
|
+
hasChanges = processedSegment.hasChanges;
|
|
31
|
+
if (processedSegment.shouldBreak) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return hasChanges ? lines.join(`
|
|
36
|
+
`) : "";
|
|
37
|
+
}
|
|
38
|
+
processUnifiedSegment(segment, lines, hasChanges, context) {
|
|
39
|
+
if (segment.type === "add") {
|
|
40
|
+
this.addUnifiedLines(segment, lines, "+ ");
|
|
41
|
+
return { hasChanges: true, shouldBreak: false };
|
|
42
|
+
}
|
|
43
|
+
if (segment.type === "remove") {
|
|
44
|
+
this.addUnifiedLines(segment, lines, "- ");
|
|
45
|
+
return { hasChanges: true, shouldBreak: false };
|
|
46
|
+
}
|
|
47
|
+
if (segment.type === "equal" && hasChanges) {
|
|
48
|
+
return this.processEqualSegment(segment, lines, context);
|
|
49
|
+
}
|
|
50
|
+
return { hasChanges, shouldBreak: false };
|
|
51
|
+
}
|
|
52
|
+
addUnifiedLines(segment, lines, prefix) {
|
|
53
|
+
for (const line of segment.value.split(`
|
|
54
|
+
`)) {
|
|
55
|
+
if (line || segment.value.endsWith(`
|
|
56
|
+
`)) {
|
|
57
|
+
lines.push(`${prefix}${line}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
processEqualSegment(segment, lines, context) {
|
|
62
|
+
const contextLines = segment.value.split(`
|
|
63
|
+
`);
|
|
64
|
+
const showLines = Math.min(context, contextLines.length);
|
|
65
|
+
for (let j = 0;j < showLines && j < contextLines.length - 1; j++) {
|
|
66
|
+
lines.push(` ${contextLines[j]}`);
|
|
67
|
+
}
|
|
68
|
+
const shouldBreak = contextLines.length > showLines;
|
|
69
|
+
return { hasChanges: true, shouldBreak };
|
|
70
|
+
}
|
|
71
|
+
formatSplit(segments) {
|
|
72
|
+
const { removedLines, addedLines } = this.extractSplitLines(segments);
|
|
73
|
+
return this.buildSplitOutput(removedLines, addedLines);
|
|
74
|
+
}
|
|
75
|
+
extractSplitLines(segments) {
|
|
76
|
+
const removedLines = [];
|
|
77
|
+
const addedLines = [];
|
|
78
|
+
for (const segment of segments) {
|
|
79
|
+
if (segment.type === "remove") {
|
|
80
|
+
this.addSplitLines(segment, removedLines);
|
|
81
|
+
} else if (segment.type === "add") {
|
|
82
|
+
this.addSplitLines(segment, addedLines);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { removedLines, addedLines };
|
|
86
|
+
}
|
|
87
|
+
addSplitLines(segment, targetLines) {
|
|
88
|
+
for (const line of segment.value.split(`
|
|
89
|
+
`)) {
|
|
90
|
+
if (line || segment.value.endsWith(`
|
|
91
|
+
`)) {
|
|
92
|
+
targetLines.push(line);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
buildSplitOutput(removedLines, addedLines) {
|
|
97
|
+
const result = [];
|
|
98
|
+
this.addSplitSection(result, removedLines, "--- Removed");
|
|
99
|
+
this.addSplitSection(result, addedLines, "+++ Added", result.length > 0);
|
|
100
|
+
return result.join(`
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
addSplitSection(result, lines, header, addSeparator = false) {
|
|
104
|
+
if (lines.length > 0) {
|
|
105
|
+
if (addSeparator) {
|
|
106
|
+
result.push("");
|
|
107
|
+
}
|
|
108
|
+
result.push(header);
|
|
109
|
+
result.push(...lines);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
formatMinimal(segments) {
|
|
113
|
+
const changes = [];
|
|
114
|
+
for (const segment of segments) {
|
|
115
|
+
if (segment.type === "add") {
|
|
116
|
+
changes.push(`+${segment.value}`);
|
|
117
|
+
} else if (segment.type === "remove") {
|
|
118
|
+
changes.push(`-${segment.value}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return changes.join("");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
DiffFormatter
|
|
126
|
+
};
|