@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
package/lib/context.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
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/context.ts
|
|
21
|
+
import debug from "debug";
|
|
22
|
+
import { BatchExecutor } from "./batch/batch-executor.js";
|
|
23
|
+
import { outputFile } from "./config.js";
|
|
24
|
+
import { logUnhandledError } from "./log.js";
|
|
25
|
+
import { Tab } from "./tab.js";
|
|
26
|
+
var testDebug = debug("pw:mcp:test");
|
|
27
|
+
|
|
28
|
+
class Context {
|
|
29
|
+
tools;
|
|
30
|
+
config;
|
|
31
|
+
sessionLog;
|
|
32
|
+
options;
|
|
33
|
+
_browserContextPromise;
|
|
34
|
+
_browserContextFactory;
|
|
35
|
+
_tabs = [];
|
|
36
|
+
_currentTab;
|
|
37
|
+
_clientInfo;
|
|
38
|
+
_batchExecutor;
|
|
39
|
+
static _allContexts = new Set;
|
|
40
|
+
_closeBrowserContextPromise;
|
|
41
|
+
_isRunningTool = false;
|
|
42
|
+
_abortController = new AbortController;
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.tools = options.tools;
|
|
45
|
+
this.config = options.config;
|
|
46
|
+
this.sessionLog = options.sessionLog;
|
|
47
|
+
this.options = options;
|
|
48
|
+
this._browserContextFactory = options.browserContextFactory;
|
|
49
|
+
this._clientInfo = options.clientInfo;
|
|
50
|
+
testDebug("create context");
|
|
51
|
+
Context._allContexts.add(this);
|
|
52
|
+
}
|
|
53
|
+
static async disposeAll() {
|
|
54
|
+
await Promise.all([...Context._allContexts].map((context) => context.dispose()));
|
|
55
|
+
}
|
|
56
|
+
tabs() {
|
|
57
|
+
return this._tabs;
|
|
58
|
+
}
|
|
59
|
+
currentTab() {
|
|
60
|
+
return this._currentTab;
|
|
61
|
+
}
|
|
62
|
+
currentTabOrDie() {
|
|
63
|
+
if (!this._currentTab) {
|
|
64
|
+
throw new Error('No open pages available. Use the "browser_navigate" tool to navigate to a page first.');
|
|
65
|
+
}
|
|
66
|
+
return this._currentTab;
|
|
67
|
+
}
|
|
68
|
+
async newTab() {
|
|
69
|
+
const { browserContext } = await this._ensureBrowserContext();
|
|
70
|
+
const page = await browserContext.newPage();
|
|
71
|
+
const tab = this._tabs.find((t) => t.page === page);
|
|
72
|
+
if (!tab) {
|
|
73
|
+
throw new Error("Failed to create tab: tab not found after creation");
|
|
74
|
+
}
|
|
75
|
+
this._currentTab = tab;
|
|
76
|
+
return this._currentTab;
|
|
77
|
+
}
|
|
78
|
+
async selectTab(index) {
|
|
79
|
+
const tab = this._tabs[index];
|
|
80
|
+
if (!tab) {
|
|
81
|
+
throw new Error(`Tab ${index} not found`);
|
|
82
|
+
}
|
|
83
|
+
await tab.page.bringToFront();
|
|
84
|
+
this._currentTab = tab;
|
|
85
|
+
return tab;
|
|
86
|
+
}
|
|
87
|
+
async ensureTab() {
|
|
88
|
+
const { browserContext } = await this._ensureBrowserContext();
|
|
89
|
+
if (!this._currentTab) {
|
|
90
|
+
await browserContext.newPage();
|
|
91
|
+
}
|
|
92
|
+
if (!this._currentTab) {
|
|
93
|
+
throw new Error("Failed to ensure tab: current tab is null after creating page");
|
|
94
|
+
}
|
|
95
|
+
return this._currentTab;
|
|
96
|
+
}
|
|
97
|
+
async closeTab(index) {
|
|
98
|
+
const tab = index === undefined ? this._currentTab : this._tabs[index];
|
|
99
|
+
if (!tab) {
|
|
100
|
+
throw new Error(`Tab ${index} not found`);
|
|
101
|
+
}
|
|
102
|
+
const url = tab.page.url();
|
|
103
|
+
await tab.page.close();
|
|
104
|
+
return url;
|
|
105
|
+
}
|
|
106
|
+
outputFile(name) {
|
|
107
|
+
return outputFile(this.config, this._clientInfo.rootPath, name);
|
|
108
|
+
}
|
|
109
|
+
_onPageCreated(page) {
|
|
110
|
+
const newTab = new Tab(this, page, (closedTab) => this._onPageClosed(closedTab));
|
|
111
|
+
this._tabs.push(newTab);
|
|
112
|
+
this._currentTab ??= newTab;
|
|
113
|
+
}
|
|
114
|
+
_onPageClosed(tab) {
|
|
115
|
+
const index = this._tabs.indexOf(tab);
|
|
116
|
+
if (index === -1) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this._tabs.splice(index, 1);
|
|
120
|
+
if (this._currentTab === tab) {
|
|
121
|
+
this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)];
|
|
122
|
+
}
|
|
123
|
+
if (!this._tabs.length) {
|
|
124
|
+
this.closeBrowserContext().catch(() => {});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async closeBrowserContext() {
|
|
128
|
+
this._closeBrowserContextPromise ??= this._closeBrowserContextImpl().catch(logUnhandledError);
|
|
129
|
+
await this._closeBrowserContextPromise;
|
|
130
|
+
this._closeBrowserContextPromise = undefined;
|
|
131
|
+
}
|
|
132
|
+
isRunningTool() {
|
|
133
|
+
return this._isRunningTool;
|
|
134
|
+
}
|
|
135
|
+
setRunningTool(isRunningTool) {
|
|
136
|
+
this._isRunningTool = isRunningTool;
|
|
137
|
+
}
|
|
138
|
+
getBatchExecutor() {
|
|
139
|
+
this._batchExecutor ??= (() => {
|
|
140
|
+
const toolRegistry = new Map;
|
|
141
|
+
for (const tool of this.tools) {
|
|
142
|
+
toolRegistry.set(tool.schema.name, tool);
|
|
143
|
+
}
|
|
144
|
+
return new BatchExecutor(this, toolRegistry);
|
|
145
|
+
})();
|
|
146
|
+
return this._batchExecutor;
|
|
147
|
+
}
|
|
148
|
+
async _closeBrowserContextImpl() {
|
|
149
|
+
if (!this._browserContextPromise) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
testDebug("close context");
|
|
153
|
+
const promise = this._browserContextPromise;
|
|
154
|
+
this._browserContextPromise = undefined;
|
|
155
|
+
await promise.then(async ({ browserContext, close }) => {
|
|
156
|
+
if (this.config.saveTrace) {
|
|
157
|
+
await browserContext.tracing.stop();
|
|
158
|
+
}
|
|
159
|
+
await close();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async dispose() {
|
|
163
|
+
this._abortController.abort("MCP context disposed");
|
|
164
|
+
await this.closeBrowserContext();
|
|
165
|
+
Context._allContexts.delete(this);
|
|
166
|
+
}
|
|
167
|
+
async _setupRequestInterception(context) {
|
|
168
|
+
if (this.config.network?.allowedOrigins?.length) {
|
|
169
|
+
await context.route("**", (route) => route.abort("blockedbyclient"));
|
|
170
|
+
await Promise.all(this.config.network.allowedOrigins.map((origin) => context.route(`*://${origin}/**`, (route) => route.continue())));
|
|
171
|
+
}
|
|
172
|
+
if (this.config.network?.blockedOrigins?.length) {
|
|
173
|
+
await Promise.all(this.config.network.blockedOrigins.map((origin) => context.route(`*://${origin}/**`, (route) => route.abort("blockedbyclient"))));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
_ensureBrowserContext() {
|
|
177
|
+
this._browserContextPromise ??= (() => {
|
|
178
|
+
const promise = this._setupBrowserContext();
|
|
179
|
+
promise.catch(() => {
|
|
180
|
+
this._browserContextPromise = undefined;
|
|
181
|
+
});
|
|
182
|
+
return promise;
|
|
183
|
+
})();
|
|
184
|
+
return this._browserContextPromise;
|
|
185
|
+
}
|
|
186
|
+
async _setupBrowserContext() {
|
|
187
|
+
if (this._closeBrowserContextPromise) {
|
|
188
|
+
throw new Error("Another browser context is being closed.");
|
|
189
|
+
}
|
|
190
|
+
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal);
|
|
191
|
+
const { browserContext } = result;
|
|
192
|
+
await this._setupRequestInterception(browserContext);
|
|
193
|
+
if (this.sessionLog) {
|
|
194
|
+
await InputRecorder.create(this, browserContext);
|
|
195
|
+
}
|
|
196
|
+
for (const page of browserContext.pages()) {
|
|
197
|
+
this._onPageCreated(page);
|
|
198
|
+
}
|
|
199
|
+
browserContext.on("page", (page) => this._onPageCreated(page));
|
|
200
|
+
if (this.config.saveTrace) {
|
|
201
|
+
await browserContext.tracing.start({
|
|
202
|
+
name: "trace",
|
|
203
|
+
screenshots: false,
|
|
204
|
+
snapshots: true,
|
|
205
|
+
sources: false
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
class InputRecorder {
|
|
213
|
+
_context;
|
|
214
|
+
_browserContext;
|
|
215
|
+
constructor(context, browserContext) {
|
|
216
|
+
this._context = context;
|
|
217
|
+
this._browserContext = browserContext;
|
|
218
|
+
}
|
|
219
|
+
static async create(context, browserContext) {
|
|
220
|
+
const recorder = new InputRecorder(context, browserContext);
|
|
221
|
+
await recorder._initialize();
|
|
222
|
+
return recorder;
|
|
223
|
+
}
|
|
224
|
+
async _initialize() {
|
|
225
|
+
const sessionLog = this._context.sessionLog;
|
|
226
|
+
if (!sessionLog) {
|
|
227
|
+
throw new Error("Session log is required for recorder initialization");
|
|
228
|
+
}
|
|
229
|
+
await this._browserContext._enableRecorder({
|
|
230
|
+
mode: "recording",
|
|
231
|
+
recorderMode: "api"
|
|
232
|
+
}, {
|
|
233
|
+
actionAdded: (page, data, code) => {
|
|
234
|
+
if (this._context.isRunningTool()) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const tab = Tab.forPage(page);
|
|
238
|
+
if (tab) {
|
|
239
|
+
sessionLog.logUserAction(data.action, tab, code, false);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
actionUpdated: (page, data, code) => {
|
|
243
|
+
if (this._context.isRunningTool()) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const tab = Tab.forPage(page);
|
|
247
|
+
if (tab) {
|
|
248
|
+
sessionLog.logUserAction(data.action, tab, code, true);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
signalAdded: (page, data) => {
|
|
252
|
+
if (this._context.isRunningTool()) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (data.signal.name !== "navigation") {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const tab = Tab.forPage(page);
|
|
259
|
+
const navigateAction = {
|
|
260
|
+
name: "navigate",
|
|
261
|
+
url: data.signal.url,
|
|
262
|
+
signals: []
|
|
263
|
+
};
|
|
264
|
+
if (tab) {
|
|
265
|
+
sessionLog.logUserAction(navigateAction, tab, `await page.goto('${data.signal.url}');`, false);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
export {
|
|
272
|
+
InputRecorder,
|
|
273
|
+
Context
|
|
274
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
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/diagnostics/common/diagnostic-base.ts
|
|
21
|
+
class DiagnosticBase {
|
|
22
|
+
isDisposed = false;
|
|
23
|
+
componentName;
|
|
24
|
+
page;
|
|
25
|
+
constructor(page, componentName) {
|
|
26
|
+
this.page = page;
|
|
27
|
+
this.componentName = componentName;
|
|
28
|
+
}
|
|
29
|
+
checkDisposed() {
|
|
30
|
+
if (this.isDisposed) {
|
|
31
|
+
throw new Error(`${this.componentName} has been disposed`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
getPage() {
|
|
35
|
+
this.checkDisposed();
|
|
36
|
+
if (!this.page) {
|
|
37
|
+
throw new Error("Page reference is null");
|
|
38
|
+
}
|
|
39
|
+
return this.page;
|
|
40
|
+
}
|
|
41
|
+
getIsDisposed() {
|
|
42
|
+
return this.isDisposed;
|
|
43
|
+
}
|
|
44
|
+
get disposed() {
|
|
45
|
+
return this.isDisposed;
|
|
46
|
+
}
|
|
47
|
+
markDisposed() {
|
|
48
|
+
this.isDisposed = true;
|
|
49
|
+
}
|
|
50
|
+
async dispose() {
|
|
51
|
+
if (this.isDisposed) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
await this.performDispose();
|
|
56
|
+
} catch {} finally {
|
|
57
|
+
this.markDisposed();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
DiagnosticBase
|
|
63
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
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/diagnostics/common/error-enrichment-utils.ts
|
|
21
|
+
import { deduplicateAndLimit } from "../../utils/array-utils.js";
|
|
22
|
+
var errorPatterns = new Map([
|
|
23
|
+
[
|
|
24
|
+
/timeout/i,
|
|
25
|
+
[
|
|
26
|
+
"Consider increasing timeout values",
|
|
27
|
+
"Check for slow network conditions",
|
|
28
|
+
"Verify element loading states"
|
|
29
|
+
]
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
/not found|element not visible/i,
|
|
33
|
+
[
|
|
34
|
+
"Verify element selector accuracy",
|
|
35
|
+
"Wait for element to become visible",
|
|
36
|
+
"Check if element is in correct frame context"
|
|
37
|
+
]
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
/not enabled|disabled/i,
|
|
41
|
+
[
|
|
42
|
+
"Wait for element to become enabled",
|
|
43
|
+
"Check element state and attributes",
|
|
44
|
+
"Verify no modal dialogs are blocking interaction"
|
|
45
|
+
]
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
/disposed|disposed/i,
|
|
49
|
+
[
|
|
50
|
+
"Component or resource was disposed prematurely",
|
|
51
|
+
"Check component lifecycle management",
|
|
52
|
+
"Ensure proper initialization before use"
|
|
53
|
+
]
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
/memory/i,
|
|
57
|
+
[
|
|
58
|
+
"Check for memory leaks or excessive resource usage",
|
|
59
|
+
"Consider more aggressive resource cleanup",
|
|
60
|
+
"Monitor memory usage patterns"
|
|
61
|
+
]
|
|
62
|
+
]
|
|
63
|
+
]);
|
|
64
|
+
function getPatternBasedSuggestions(message) {
|
|
65
|
+
const suggestions = [];
|
|
66
|
+
for (const [pattern, patternSuggestions] of errorPatterns) {
|
|
67
|
+
if (pattern.test(message)) {
|
|
68
|
+
suggestions.push(...patternSuggestions);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return suggestions;
|
|
72
|
+
}
|
|
73
|
+
function getContextSpecificSuggestions(context) {
|
|
74
|
+
const suggestions = [];
|
|
75
|
+
if (context.executionTime && context.executionTime > 5000) {
|
|
76
|
+
suggestions.push("Long execution time detected - consider optimization");
|
|
77
|
+
}
|
|
78
|
+
if (context.selector) {
|
|
79
|
+
suggestions.push(`Failed selector: ${context.selector}`);
|
|
80
|
+
if (context.selector.includes("#")) {
|
|
81
|
+
suggestions.push("ID selectors may be fragile - consider alternatives");
|
|
82
|
+
}
|
|
83
|
+
if (context.selector.includes("nth-child")) {
|
|
84
|
+
suggestions.push("Position-based selectors are fragile - use semantic selectors");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (context.component === "PageAnalyzer") {
|
|
88
|
+
suggestions.push("Consider using parallel analysis for complex pages");
|
|
89
|
+
}
|
|
90
|
+
if (context.operation.includes("iframe")) {
|
|
91
|
+
suggestions.push("Check iframe accessibility and cross-origin restrictions");
|
|
92
|
+
}
|
|
93
|
+
return suggestions;
|
|
94
|
+
}
|
|
95
|
+
function generateSuggestions(error, context) {
|
|
96
|
+
const suggestions = [];
|
|
97
|
+
const message = error.message.toLowerCase();
|
|
98
|
+
suggestions.push(...getPatternBasedSuggestions(message));
|
|
99
|
+
if (context) {
|
|
100
|
+
suggestions.push(...getContextSpecificSuggestions(context));
|
|
101
|
+
}
|
|
102
|
+
return deduplicateAndLimit(suggestions, 5);
|
|
103
|
+
}
|
|
104
|
+
async function safeDispose(resource, _resourceType, _operation) {
|
|
105
|
+
try {
|
|
106
|
+
await resource.dispose();
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.warn(`Failed to dispose ${_resourceType} during ${_operation}:`, error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function safeDisposeAll(resources, resourceType, operation) {
|
|
112
|
+
const disposePromises = resources.map((resource) => safeDispose(resource, resourceType, operation));
|
|
113
|
+
await Promise.allSettled(disposePromises);
|
|
114
|
+
}
|
|
115
|
+
function createEnrichedError(originalError, context, additionalSuggestions = []) {
|
|
116
|
+
const suggestions = [
|
|
117
|
+
...generateSuggestions(originalError, context),
|
|
118
|
+
...additionalSuggestions
|
|
119
|
+
];
|
|
120
|
+
const enhancedError = new Error(originalError.message);
|
|
121
|
+
enhancedError.originalError = originalError;
|
|
122
|
+
enhancedError.context = context;
|
|
123
|
+
enhancedError.suggestions = suggestions;
|
|
124
|
+
return {
|
|
125
|
+
enhancedError,
|
|
126
|
+
suggestions,
|
|
127
|
+
contextInfo: {
|
|
128
|
+
component: context.component,
|
|
129
|
+
operation: context.operation,
|
|
130
|
+
executionTime: context.executionTime,
|
|
131
|
+
timestamp: Date.now()
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function analyzePatternFrequency(errors) {
|
|
136
|
+
const patternCounts = new Map;
|
|
137
|
+
const patternSuggestions = new Map;
|
|
138
|
+
for (const { error } of errors) {
|
|
139
|
+
const message = error.message.toLowerCase();
|
|
140
|
+
for (const [pattern, suggestions] of errorPatterns) {
|
|
141
|
+
if (pattern.test(message)) {
|
|
142
|
+
const patternStr = pattern.source;
|
|
143
|
+
patternCounts.set(patternStr, (patternCounts.get(patternStr) ?? 0) + 1);
|
|
144
|
+
if (!patternSuggestions.has(patternStr)) {
|
|
145
|
+
patternSuggestions.set(patternStr, new Set);
|
|
146
|
+
}
|
|
147
|
+
for (const s of suggestions) {
|
|
148
|
+
patternSuggestions.get(patternStr)?.add(s);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { patternCounts, patternSuggestions };
|
|
154
|
+
}
|
|
155
|
+
function buildFrequentPatterns(patternCounts, patternSuggestions) {
|
|
156
|
+
return Array.from(patternCounts.entries()).filter(([, count]) => count > 1).sort(([, a], [, b]) => b - a).map(([pattern, count]) => ({
|
|
157
|
+
pattern,
|
|
158
|
+
count,
|
|
159
|
+
suggestions: Array.from(patternSuggestions.get(pattern) ?? [])
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
function analyzeTimeBasedPatterns(errors) {
|
|
163
|
+
const now = Date.now();
|
|
164
|
+
const oneHour = 60 * 60 * 1000;
|
|
165
|
+
const recentErrors = errors.filter((e) => now - e.timestamp < oneHour);
|
|
166
|
+
return {
|
|
167
|
+
recentErrors: recentErrors.length,
|
|
168
|
+
errorRate: errors.length > 0 ? recentErrors.length / errors.length : 0
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function analyzeComponentErrors(errors) {
|
|
172
|
+
const componentAnalysis = {};
|
|
173
|
+
for (const { context } of errors) {
|
|
174
|
+
componentAnalysis[context.component] = (componentAnalysis[context.component] ?? 0) + 1;
|
|
175
|
+
}
|
|
176
|
+
return componentAnalysis;
|
|
177
|
+
}
|
|
178
|
+
function analyzeErrorPatterns(errors) {
|
|
179
|
+
const { patternCounts, patternSuggestions } = analyzePatternFrequency(errors);
|
|
180
|
+
const frequentPatterns = buildFrequentPatterns(patternCounts, patternSuggestions);
|
|
181
|
+
const timeBasedAnalysis = analyzeTimeBasedPatterns(errors);
|
|
182
|
+
const componentAnalysis = analyzeComponentErrors(errors);
|
|
183
|
+
return {
|
|
184
|
+
frequentPatterns,
|
|
185
|
+
timeBasedAnalysis,
|
|
186
|
+
componentAnalysis
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function generateRecoverySuggestions(errorAnalysis) {
|
|
190
|
+
const suggestions = [];
|
|
191
|
+
if (errorAnalysis.timeBasedAnalysis.errorRate > 0.5) {
|
|
192
|
+
suggestions.push("High error rate detected - review recent changes");
|
|
193
|
+
}
|
|
194
|
+
if (errorAnalysis.frequentPatterns.length > 0) {
|
|
195
|
+
const mostFrequent = errorAnalysis.frequentPatterns[0];
|
|
196
|
+
suggestions.push(`Most frequent error pattern: ${mostFrequent.pattern} (${mostFrequent.count} occurrences)`);
|
|
197
|
+
suggestions.push(...mostFrequent.suggestions.slice(0, 2));
|
|
198
|
+
}
|
|
199
|
+
const componentWithMostErrors = Object.entries(errorAnalysis.componentAnalysis).sort(([, a], [, b]) => b - a)[0];
|
|
200
|
+
if (componentWithMostErrors && componentWithMostErrors[1] > 3) {
|
|
201
|
+
suggestions.push(`${componentWithMostErrors[0]} component has frequent errors - review implementation`);
|
|
202
|
+
}
|
|
203
|
+
return suggestions.slice(0, 5);
|
|
204
|
+
}
|
|
205
|
+
export {
|
|
206
|
+
safeDisposeAll,
|
|
207
|
+
safeDispose,
|
|
208
|
+
generateSuggestions,
|
|
209
|
+
generateRecoverySuggestions,
|
|
210
|
+
createEnrichedError,
|
|
211
|
+
analyzeErrorPatterns
|
|
212
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
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/diagnostics/common/index.ts
|
|
21
|
+
import {
|
|
22
|
+
DiagnosticBase
|
|
23
|
+
} from "./diagnostic-base.js";
|
|
24
|
+
import {
|
|
25
|
+
analyzeErrorPatterns,
|
|
26
|
+
createEnrichedError,
|
|
27
|
+
generateRecoverySuggestions,
|
|
28
|
+
generateSuggestions,
|
|
29
|
+
safeDispose,
|
|
30
|
+
safeDisposeAll
|
|
31
|
+
} from "./error-enrichment-utils.js";
|
|
32
|
+
import {
|
|
33
|
+
createAdvancedStage,
|
|
34
|
+
createCoreStage,
|
|
35
|
+
createDependentStage,
|
|
36
|
+
InitializationManager
|
|
37
|
+
} from "./initialization-manager.js";
|
|
38
|
+
import {
|
|
39
|
+
globalPerformanceTracker,
|
|
40
|
+
PerformanceTracker
|
|
41
|
+
} from "./performance-tracker.js";
|
|
42
|
+
export {
|
|
43
|
+
safeDisposeAll,
|
|
44
|
+
safeDispose,
|
|
45
|
+
globalPerformanceTracker,
|
|
46
|
+
generateSuggestions,
|
|
47
|
+
generateRecoverySuggestions,
|
|
48
|
+
createEnrichedError,
|
|
49
|
+
createDependentStage,
|
|
50
|
+
createCoreStage,
|
|
51
|
+
createAdvancedStage,
|
|
52
|
+
analyzeErrorPatterns,
|
|
53
|
+
PerformanceTracker,
|
|
54
|
+
InitializationManager,
|
|
55
|
+
DiagnosticBase
|
|
56
|
+
};
|