@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.
Files changed (107) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1047 -0
  3. package/cli.js +18 -0
  4. package/config.d.ts +124 -0
  5. package/index.d.ts +25 -0
  6. package/index.js +18 -0
  7. package/lib/actions.d.js +0 -0
  8. package/lib/batch/batch-executor.js +137 -0
  9. package/lib/browser-context-factory.js +252 -0
  10. package/lib/browser-server-backend.js +139 -0
  11. package/lib/config/constants.js +80 -0
  12. package/lib/config.js +405 -0
  13. package/lib/context.js +274 -0
  14. package/lib/diagnostics/common/diagnostic-base.js +63 -0
  15. package/lib/diagnostics/common/error-enrichment-utils.js +212 -0
  16. package/lib/diagnostics/common/index.js +56 -0
  17. package/lib/diagnostics/common/initialization-manager.js +210 -0
  18. package/lib/diagnostics/common/performance-tracker.js +132 -0
  19. package/lib/diagnostics/diagnostic-error.js +140 -0
  20. package/lib/diagnostics/diagnostic-level.js +123 -0
  21. package/lib/diagnostics/diagnostic-thresholds.js +347 -0
  22. package/lib/diagnostics/element-discovery.js +441 -0
  23. package/lib/diagnostics/enhanced-error-handler.js +376 -0
  24. package/lib/diagnostics/error-enrichment.js +157 -0
  25. package/lib/diagnostics/frame-reference-manager.js +179 -0
  26. package/lib/diagnostics/page-analyzer.js +639 -0
  27. package/lib/diagnostics/parallel-page-analyzer.js +129 -0
  28. package/lib/diagnostics/resource-manager.js +134 -0
  29. package/lib/diagnostics/smart-config.js +482 -0
  30. package/lib/diagnostics/smart-handle.js +118 -0
  31. package/lib/diagnostics/unified-system.js +717 -0
  32. package/lib/extension/cdp-relay.js +486 -0
  33. package/lib/extension/extension-context-factory.js +74 -0
  34. package/lib/extension/main.js +41 -0
  35. package/lib/file-utils.js +42 -0
  36. package/lib/generate-keys.js +75 -0
  37. package/lib/http-server.js +50 -0
  38. package/lib/in-process-client.js +64 -0
  39. package/lib/index.js +48 -0
  40. package/lib/javascript.js +90 -0
  41. package/lib/log.js +33 -0
  42. package/lib/loop/loop-claude.js +247 -0
  43. package/lib/loop/loop-open-ai.js +222 -0
  44. package/lib/loop/loop.js +174 -0
  45. package/lib/loop/main.js +46 -0
  46. package/lib/loopTools/context.js +76 -0
  47. package/lib/loopTools/main.js +65 -0
  48. package/lib/loopTools/perform.js +40 -0
  49. package/lib/loopTools/snapshot.js +37 -0
  50. package/lib/loopTools/tool.js +26 -0
  51. package/lib/manual-promise.js +125 -0
  52. package/lib/mcp/in-process-transport.js +91 -0
  53. package/lib/mcp/proxy-backend.js +127 -0
  54. package/lib/mcp/server.js +123 -0
  55. package/lib/mcp/transport.js +159 -0
  56. package/lib/package.js +28 -0
  57. package/lib/program.js +82 -0
  58. package/lib/response.js +493 -0
  59. package/lib/schemas/expectation.js +152 -0
  60. package/lib/session-log.js +210 -0
  61. package/lib/tab.js +417 -0
  62. package/lib/tools/base-tool-handler.js +141 -0
  63. package/lib/tools/batch-execute.js +150 -0
  64. package/lib/tools/common.js +65 -0
  65. package/lib/tools/console.js +60 -0
  66. package/lib/tools/diagnose/diagnose-analysis-runner.js +101 -0
  67. package/lib/tools/diagnose/diagnose-config-handler.js +130 -0
  68. package/lib/tools/diagnose/diagnose-report-builder.js +394 -0
  69. package/lib/tools/diagnose.js +147 -0
  70. package/lib/tools/dialogs.js +57 -0
  71. package/lib/tools/evaluate.js +67 -0
  72. package/lib/tools/files.js +53 -0
  73. package/lib/tools/find-elements.js +307 -0
  74. package/lib/tools/install.js +60 -0
  75. package/lib/tools/keyboard.js +93 -0
  76. package/lib/tools/mouse.js +110 -0
  77. package/lib/tools/navigate.js +82 -0
  78. package/lib/tools/network.js +50 -0
  79. package/lib/tools/pdf.js +46 -0
  80. package/lib/tools/screenshot.js +113 -0
  81. package/lib/tools/snapshot.js +158 -0
  82. package/lib/tools/tabs.js +97 -0
  83. package/lib/tools/tool.js +47 -0
  84. package/lib/tools/utils.js +131 -0
  85. package/lib/tools/wait.js +64 -0
  86. package/lib/tools.js +65 -0
  87. package/lib/types/batch.js +47 -0
  88. package/lib/types/diff.js +0 -0
  89. package/lib/types/performance.js +0 -0
  90. package/lib/types/threshold-base.js +0 -0
  91. package/lib/utils/array-utils.js +44 -0
  92. package/lib/utils/code-deduplication-utils.js +141 -0
  93. package/lib/utils/common-formatters.js +252 -0
  94. package/lib/utils/console-filter.js +64 -0
  95. package/lib/utils/diagnostic-report-utils.js +178 -0
  96. package/lib/utils/diff-formatter.js +126 -0
  97. package/lib/utils/disposable-manager.js +135 -0
  98. package/lib/utils/error-handler-middleware.js +77 -0
  99. package/lib/utils/image-processor.js +137 -0
  100. package/lib/utils/index.js +92 -0
  101. package/lib/utils/report-builder.js +189 -0
  102. package/lib/utils/request-logger.js +82 -0
  103. package/lib/utils/response-diff-detector.js +150 -0
  104. package/lib/utils/section-builder.js +62 -0
  105. package/lib/utils/tool-patterns.js +153 -0
  106. package/lib/utils.js +46 -0
  107. package/package.json +77 -0
@@ -0,0 +1,376 @@
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/enhanced-error-handler.ts
21
+ import { DiagnosticBase } from "./common/index.js";
22
+ import { DiagnosticError } from "./diagnostic-error.js";
23
+ import { DiagnosticLevelManager } from "./diagnostic-level.js";
24
+ import { ElementDiscovery } from "./element-discovery.js";
25
+ import { ErrorEnrichment } from "./error-enrichment.js";
26
+ import { PageAnalyzer } from "./page-analyzer.js";
27
+
28
+ class EnhancedErrorHandler extends DiagnosticBase {
29
+ pageAnalyzer;
30
+ elementDiscovery;
31
+ errorEnrichment;
32
+ diagnosticManager;
33
+ errorHistory = [];
34
+ maxErrorHistory;
35
+ constructor(page, diagnosticConfig) {
36
+ super(page, "EnhancedErrorHandler");
37
+ this.pageAnalyzer = new PageAnalyzer(page);
38
+ this.elementDiscovery = new ElementDiscovery(page);
39
+ this.errorEnrichment = new ErrorEnrichment(page);
40
+ this.diagnosticManager = new DiagnosticLevelManager(diagnosticConfig);
41
+ this.maxErrorHistory = diagnosticConfig?.maxErrorHistory ?? 100;
42
+ }
43
+ async performDispose() {
44
+ await this.pageAnalyzer.dispose();
45
+ await this.elementDiscovery.dispose();
46
+ await this.errorEnrichment.dispose();
47
+ }
48
+ async enhancePlaywrightError(options) {
49
+ const { error, operation, selector, context } = options;
50
+ if (this.diagnosticManager.shouldSkipDiagnostics()) {
51
+ return error;
52
+ }
53
+ if (selector && context?.searchCriteria && this.diagnosticManager.shouldEnableFeature("alternativeSuggestions")) {
54
+ const enrichedError = await this.errorEnrichment.enrichElementNotFoundError({
55
+ originalError: error,
56
+ selector,
57
+ searchCriteria: context.searchCriteria,
58
+ maxAlternatives: this.diagnosticManager.getMaxAlternatives()
59
+ });
60
+ return enrichedError;
61
+ }
62
+ if (error.message.includes("Timeout")) {
63
+ return this.enhanceTimeoutError({
64
+ error,
65
+ operation,
66
+ selector,
67
+ timeout: context?.timeout ?? 30000
68
+ });
69
+ }
70
+ let pageStructure;
71
+ if (this.diagnosticManager.shouldEnableFeature("pageAnalysis")) {
72
+ pageStructure = await this.pageAnalyzer.analyzePageStructure();
73
+ }
74
+ const suggestions = pageStructure ? this.generateGeneralSuggestions(error, operation, pageStructure) : [];
75
+ const enhancedError = new Error(error.message);
76
+ enhancedError.originalError = error;
77
+ if (pageStructure) {
78
+ enhancedError.pageStructure = pageStructure;
79
+ }
80
+ enhancedError.suggestions = suggestions;
81
+ return enhancedError;
82
+ }
83
+ async enhanceTimeoutError(options) {
84
+ const { error, operation, selector } = options;
85
+ const enrichedError = await this.errorEnrichment.enrichTimeoutError({
86
+ originalError: error,
87
+ operation,
88
+ selector
89
+ });
90
+ const contextInfo = this.analyzeFrameContext();
91
+ enrichedError.contextInfo = contextInfo;
92
+ return enrichedError;
93
+ }
94
+ async enhanceContextError(options) {
95
+ const { error, expectedContext } = options;
96
+ const contextInfo = this.analyzeFrameContext();
97
+ const pageStructure = await this.pageAnalyzer.analyzePageStructure();
98
+ const suggestions = [
99
+ `Expected element in ${expectedContext} context`,
100
+ `Found ${contextInfo.availableFrames} available frames`,
101
+ "Try switching to the correct frame context"
102
+ ];
103
+ if (pageStructure.iframes.detected) {
104
+ suggestions.push("element might be in a different frame - use frameLocator()");
105
+ }
106
+ const enhancedError = new Error(error.message);
107
+ enhancedError.originalError = error;
108
+ enhancedError.contextInfo = contextInfo;
109
+ enhancedError.pageStructure = pageStructure;
110
+ enhancedError.suggestions = suggestions;
111
+ return enhancedError;
112
+ }
113
+ async enhancePerformanceError(options) {
114
+ const { operation, executionTime, performanceThreshold } = options;
115
+ const pageStructure = await this.pageAnalyzer.analyzePageStructure();
116
+ const exceededThreshold = executionTime > performanceThreshold;
117
+ const performanceInfo = {
118
+ executionTime,
119
+ exceededThreshold,
120
+ threshold: performanceThreshold
121
+ };
122
+ const suggestions = [
123
+ `Operation took longer than expected (${executionTime}ms vs ${performanceThreshold}ms threshold)`,
124
+ "Consider optimizing page load performance",
125
+ "Check for heavy JavaScript execution or network delays"
126
+ ];
127
+ if (pageStructure.modalStates.blockedBy.length > 0) {
128
+ suggestions.push("Modal dialogs may be causing delays");
129
+ }
130
+ const error = new Error(`Performance issue: ${operation} operation exceeded threshold`);
131
+ error.performanceInfo = performanceInfo;
132
+ error.pageStructure = pageStructure;
133
+ error.suggestions = suggestions;
134
+ return error;
135
+ }
136
+ async enhanceToolError(options) {
137
+ const { toolName, error, toolArgs } = options;
138
+ const pageStructure = await this.pageAnalyzer.analyzePageStructure();
139
+ const toolContext = {
140
+ toolName,
141
+ toolArgs: toolArgs ?? {}
142
+ };
143
+ const suggestions = this.generateToolSpecificSuggestions(toolName, error, pageStructure);
144
+ const enhancedError = new Error(error.message);
145
+ enhancedError.originalError = error;
146
+ enhancedError.toolContext = toolContext;
147
+ enhancedError.pageStructure = pageStructure;
148
+ enhancedError.suggestions = suggestions;
149
+ return enhancedError;
150
+ }
151
+ analyzeFrameContext() {
152
+ const page = this.getPage();
153
+ const frames = page.frames();
154
+ const mainFrame = page.mainFrame();
155
+ return {
156
+ availableFrames: frames.length,
157
+ currentFrame: mainFrame.name() ?? "main"
158
+ };
159
+ }
160
+ generateGeneralSuggestions(error, operation, pageStructure) {
161
+ const suggestions = [];
162
+ if (pageStructure.modalStates.blockedBy.length > 0) {
163
+ suggestions.push(`Page has active modal - handle before performing ${operation}`);
164
+ }
165
+ if (pageStructure.iframes.detected) {
166
+ suggestions.push("Check if target element is inside an iframe");
167
+ }
168
+ if (error.message.includes("not found")) {
169
+ suggestions.push("Element selector might be incorrect or element not yet loaded");
170
+ suggestions.push("Try waiting for element to be visible before interacting");
171
+ }
172
+ return suggestions;
173
+ }
174
+ generateToolSpecificSuggestions(toolName, error, pageStructure) {
175
+ const suggestions = [];
176
+ switch (toolName) {
177
+ case "browser_click":
178
+ if (error.message.includes("not enabled")) {
179
+ suggestions.push("Element appears to be disabled");
180
+ suggestions.push("Wait for element to become enabled or check if it should be enabled");
181
+ }
182
+ if (error.message.includes("not visible")) {
183
+ suggestions.push("Element is not visible - check CSS display/visibility properties");
184
+ }
185
+ break;
186
+ case "browser_type":
187
+ if (error.message.includes("not editable")) {
188
+ suggestions.push("Element is not editable - ensure it is an input field");
189
+ suggestions.push("Check if element has readonly attribute");
190
+ }
191
+ break;
192
+ case "browser_select_option":
193
+ suggestions.push("Verify that the select element contains the specified option");
194
+ suggestions.push("Check option values and text content");
195
+ break;
196
+ default:
197
+ suggestions.push(`Consider tool-specific requirements for ${toolName}`);
198
+ }
199
+ if (pageStructure.modalStates.blockedBy.length > 0) {
200
+ suggestions.push(`Modal state blocking ${toolName} operation`);
201
+ }
202
+ return suggestions;
203
+ }
204
+ createDiagnosticError(error, component, operation, executionTime, memoryUsage) {
205
+ const diagnosticError = DiagnosticError.from(error, component, operation, {
206
+ executionTime,
207
+ memoryUsage,
208
+ performanceImpact: executionTime && executionTime > 1000 ? "high" : "low"
209
+ });
210
+ this.addToErrorHistory(diagnosticError, component);
211
+ return diagnosticError;
212
+ }
213
+ async processUnifiedError(error, component, operation, context) {
214
+ const startTime = Date.now();
215
+ try {
216
+ let diagnosticError;
217
+ if (error instanceof DiagnosticError) {
218
+ diagnosticError = error;
219
+ } else {
220
+ diagnosticError = this.createDiagnosticError(error, component, operation, context?.executionTime, context?.memoryUsage);
221
+ }
222
+ if (context?.performanceThreshold && context?.executionTime && context.executionTime > context.performanceThreshold) {
223
+ const perfError = DiagnosticError.performance(`Operation ${operation} exceeded performance threshold`, component, operation, context.executionTime, context.performanceThreshold);
224
+ diagnosticError.suggestions.push(...perfError.suggestions);
225
+ }
226
+ if (this.diagnosticManager.shouldEnableFeature("alternativeSuggestions")) {
227
+ const contextualSuggestions = await this.generateContextualSuggestions(diagnosticError, component, operation, context);
228
+ diagnosticError.suggestions.push(...contextualSuggestions);
229
+ }
230
+ const similarErrors = this.findSimilarErrors(diagnosticError, component);
231
+ if (similarErrors.length > 0) {
232
+ const patternSuggestions = this.generatePatternBasedSuggestions(similarErrors);
233
+ diagnosticError.suggestions.push(...patternSuggestions);
234
+ }
235
+ return diagnosticError;
236
+ } catch {
237
+ return DiagnosticError.from(error, component, operation, {
238
+ executionTime: Date.now() - startTime
239
+ });
240
+ }
241
+ }
242
+ async generateContextualSuggestions(_error, component, operation, context) {
243
+ const suggestions = [];
244
+ try {
245
+ const pageStructure = await this.pageAnalyzer.analyzePageStructure();
246
+ this.addComponentSpecificSuggestions(suggestions, component, pageStructure, context);
247
+ this.addOperationSpecificSuggestions(suggestions, operation, pageStructure);
248
+ } catch {}
249
+ return suggestions;
250
+ }
251
+ addComponentSpecificSuggestions(suggestions, component, pageStructure, context) {
252
+ switch (component) {
253
+ case "PageAnalyzer":
254
+ this.addPageAnalyzerSuggestions(suggestions, pageStructure);
255
+ break;
256
+ case "ElementDiscovery":
257
+ this.addElementDiscoverySuggestions(suggestions, pageStructure, context);
258
+ break;
259
+ case "ResourceManager":
260
+ this.addResourceManagerSuggestions(suggestions, context);
261
+ break;
262
+ default:
263
+ break;
264
+ }
265
+ }
266
+ addPageAnalyzerSuggestions(suggestions, pageStructure) {
267
+ if (pageStructure.elements.totalVisible > 1e4) {
268
+ suggestions.push("Page has many elements - consider using parallel analysis");
269
+ }
270
+ if (pageStructure.iframes.detected) {
271
+ suggestions.push("Multiple iframes detected - they may affect analysis performance");
272
+ }
273
+ }
274
+ addElementDiscoverySuggestions(suggestions, pageStructure, context) {
275
+ if (context?.selector && pageStructure.elements.missingAria > 0) {
276
+ suggestions.push("Many elements lack ARIA attributes - try text-based selectors");
277
+ }
278
+ if (pageStructure.modalStates.blockedBy.length > 0) {
279
+ suggestions.push("Modal dialogs may be hiding target elements");
280
+ }
281
+ }
282
+ addResourceManagerSuggestions(suggestions, context) {
283
+ if (context?.memoryUsage && typeof context.memoryUsage === "number" && context.memoryUsage > 50 * 1024 * 1024) {
284
+ suggestions.push("High memory usage detected - consider more aggressive cleanup");
285
+ }
286
+ }
287
+ addOperationSpecificSuggestions(suggestions, operation, pageStructure) {
288
+ if (operation.includes("parallel") && pageStructure.elements.totalVisible < 1000) {
289
+ suggestions.push("Parallel analysis may not be beneficial for simple pages");
290
+ }
291
+ if (operation.includes("timeout")) {
292
+ suggestions.push("Consider adjusting timeout thresholds based on page complexity");
293
+ }
294
+ }
295
+ addToErrorHistory(error, component) {
296
+ this.errorHistory.push({
297
+ error,
298
+ timestamp: Date.now(),
299
+ component,
300
+ resolved: false
301
+ });
302
+ if (this.errorHistory.length > this.maxErrorHistory) {
303
+ this.errorHistory = this.errorHistory.slice(-this.maxErrorHistory);
304
+ }
305
+ }
306
+ findSimilarErrors(error, component) {
307
+ const timeWindow = 300000;
308
+ const now = Date.now();
309
+ return this.errorHistory.filter((entry) => entry.component === component && entry.error.operation === error.operation && now - entry.timestamp < timeWindow).map((entry) => entry.error).slice(-5);
310
+ }
311
+ generatePatternBasedSuggestions(similarErrors) {
312
+ const suggestions = [];
313
+ if (similarErrors.length >= 3) {
314
+ suggestions.push(`This error has occurred ${similarErrors.length} times recently - consider reviewing the operation`);
315
+ const commonSuggestions = this.findCommonSuggestions(similarErrors);
316
+ if (commonSuggestions.length > 0) {
317
+ suggestions.push("Common resolution patterns:");
318
+ suggestions.push(...commonSuggestions.slice(0, 3));
319
+ }
320
+ }
321
+ return suggestions;
322
+ }
323
+ findCommonSuggestions(errors) {
324
+ const suggestionCounts = new Map;
325
+ for (const error of errors) {
326
+ for (const suggestion of error.suggestions) {
327
+ const count = suggestionCounts.get(suggestion) ?? 0;
328
+ suggestionCounts.set(suggestion, count + 1);
329
+ }
330
+ }
331
+ return Array.from(suggestionCounts.entries()).filter(([, count]) => count > 1).sort(([, a], [, b]) => b - a).map(([suggestion]) => suggestion);
332
+ }
333
+ markErrorResolved(errorId) {
334
+ const entry = this.errorHistory.find((e) => e.error.timestamp.toString() === errorId || e.error.message.includes(errorId));
335
+ if (entry) {
336
+ entry.resolved = true;
337
+ }
338
+ }
339
+ getErrorStatistics() {
340
+ const now = Date.now();
341
+ const recentTimeWindow = 600000;
342
+ const recentErrors = this.errorHistory.filter((e) => now - e.timestamp < recentTimeWindow);
343
+ const resolvedErrors = this.errorHistory.filter((e) => e.resolved);
344
+ const errorsByComponent = {
345
+ PageAnalyzer: 0,
346
+ ElementDiscovery: 0,
347
+ ResourceManager: 0,
348
+ ErrorHandler: 0,
349
+ ConfigManager: 0,
350
+ UnifiedSystem: 0,
351
+ InitializationManager: 0
352
+ };
353
+ const errorsByOperation = {};
354
+ for (const entry of this.errorHistory) {
355
+ errorsByComponent[entry.component]++;
356
+ const operation = entry.error.operation;
357
+ errorsByOperation[operation] = (errorsByOperation[operation] ?? 0) + 1;
358
+ }
359
+ return {
360
+ totalErrors: this.errorHistory.length,
361
+ errorsByComponent,
362
+ errorsByOperation,
363
+ resolutionRate: this.errorHistory.length > 0 ? resolvedErrors.length / this.errorHistory.length : 1,
364
+ recentErrorRate: recentErrors.length / Math.max(this.errorHistory.length, 1)
365
+ };
366
+ }
367
+ clearErrorHistory() {
368
+ this.errorHistory = [];
369
+ }
370
+ getRecentErrors(limit = 10) {
371
+ return this.errorHistory.slice(-limit);
372
+ }
373
+ }
374
+ export {
375
+ EnhancedErrorHandler
376
+ };
@@ -0,0 +1,157 @@
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/error-enrichment.ts
21
+ import { deduplicate } from "../utils/array-utils.js";
22
+ import { createDisposableManager } from "../utils/disposable-manager.js";
23
+ import { DiagnosticBase } from "./common/diagnostic-base.js";
24
+ import { generateSuggestions } from "./common/error-enrichment-utils.js";
25
+ import { ElementDiscovery } from "./element-discovery.js";
26
+ import { PageAnalyzer } from "./page-analyzer.js";
27
+
28
+ class ErrorEnrichment extends DiagnosticBase {
29
+ pageAnalyzer;
30
+ elementDiscovery;
31
+ disposableManager = createDisposableManager("ErrorEnrichment");
32
+ constructor(page) {
33
+ super(page, "ErrorEnrichment");
34
+ this.pageAnalyzer = this.disposableManager.register(new PageAnalyzer(page));
35
+ this.elementDiscovery = this.disposableManager.register(new ElementDiscovery(page));
36
+ }
37
+ async performDispose() {
38
+ await this.disposableManager.dispose();
39
+ }
40
+ async enrichElementNotFoundError(options) {
41
+ const { originalError, selector, searchCriteria, maxAlternatives } = options;
42
+ const [alternatives, pageStructure] = await Promise.all([
43
+ searchCriteria ? this.elementDiscovery.findAlternativeElements({
44
+ originalSelector: selector,
45
+ searchCriteria,
46
+ maxResults: maxAlternatives
47
+ }) : Promise.resolve([]),
48
+ this.pageAnalyzer.analyzePageStructure()
49
+ ]);
50
+ const suggestions = this.generateElementNotFoundSuggestions(pageStructure, alternatives);
51
+ const enrichedError = new Error(this.enhanceErrorMessage(originalError, alternatives));
52
+ enrichedError.originalError = originalError;
53
+ enrichedError.alternatives = alternatives;
54
+ enrichedError.pageStructure = pageStructure;
55
+ enrichedError.diagnosticInfo = pageStructure;
56
+ enrichedError.suggestions = suggestions;
57
+ return enrichedError;
58
+ }
59
+ async enrichTimeoutError(options) {
60
+ const { originalError, operation, selector } = options;
61
+ const pageStructure = await this.pageAnalyzer.analyzePageStructure();
62
+ const suggestions = this.generateTimeoutSuggestions(pageStructure, operation, selector);
63
+ const enrichedError = new Error(originalError.message);
64
+ enrichedError.originalError = originalError;
65
+ enrichedError.pageStructure = pageStructure;
66
+ enrichedError.diagnosticInfo = pageStructure;
67
+ enrichedError.suggestions = suggestions;
68
+ return enrichedError;
69
+ }
70
+ async enrichBatchFailureError(options) {
71
+ const { originalError, failedStep, executedSteps } = options;
72
+ const pageStructure = await this.pageAnalyzer.analyzePageStructure();
73
+ const suggestions = this.generateBatchFailureSuggestions(pageStructure, failedStep);
74
+ const enrichedError = new Error(originalError.message);
75
+ enrichedError.originalError = originalError;
76
+ enrichedError.pageStructure = pageStructure;
77
+ enrichedError.diagnosticInfo = pageStructure;
78
+ enrichedError.suggestions = suggestions;
79
+ enrichedError.batchContext = {
80
+ failedStep,
81
+ executedSteps
82
+ };
83
+ return enrichedError;
84
+ }
85
+ enhanceErrorMessage(originalError, alternatives) {
86
+ let message = originalError.message;
87
+ if (alternatives.length > 0) {
88
+ message += `
89
+
90
+ Alternative elements found:`;
91
+ for (const [index, alt] of alternatives.entries()) {
92
+ message += `
93
+ ${index + 1}. ${alt.selector} (confidence: ${(alt.confidence * 100).toFixed(0)}%) - ${alt.reason}`;
94
+ }
95
+ }
96
+ return message;
97
+ }
98
+ generateElementNotFoundSuggestions(pageStructure, alternatives) {
99
+ const suggestions = [];
100
+ if (alternatives.length > 0) {
101
+ suggestions.push(`Try using one of the ${alternatives.length} alternative elements found`);
102
+ if (alternatives[0].confidence > 0.8) {
103
+ suggestions.push(`High confidence match available: ${alternatives[0].selector}`);
104
+ }
105
+ }
106
+ const commonSuggestions = generateSuggestions(new Error("Element not found"), {
107
+ operation: "findElement",
108
+ component: "ErrorEnrichment"
109
+ });
110
+ suggestions.push(...commonSuggestions);
111
+ if (pageStructure.iframes.detected) {
112
+ suggestions.push("Element might be inside an iframe");
113
+ if (pageStructure.iframes.inaccessible.length > 0) {
114
+ suggestions.push("Some iframes are not accessible - check cross-origin restrictions");
115
+ }
116
+ }
117
+ if (pageStructure.modalStates.blockedBy.length > 0) {
118
+ suggestions.push("Page has active modal dialog - handle it first");
119
+ }
120
+ if (pageStructure.elements.missingAria > 0) {
121
+ suggestions.push("Some elements lack proper ARIA attributes - consider using text-based selectors");
122
+ }
123
+ return deduplicate(suggestions);
124
+ }
125
+ generateTimeoutSuggestions(pageStructure, operation, selector) {
126
+ const commonSuggestions = generateSuggestions(new Error("timeout"), {
127
+ operation,
128
+ component: "ErrorEnrichment",
129
+ selector
130
+ });
131
+ const suggestions = [...commonSuggestions];
132
+ if (pageStructure.modalStates.blockedBy.length > 0) {
133
+ suggestions.push(`Page has active modal dialog - handle it before performing ${operation}`);
134
+ }
135
+ if (pageStructure.iframes.detected) {
136
+ suggestions.push("Element might be inside an iframe");
137
+ }
138
+ suggestions.push(`Wait for page load completion before performing ${operation}`);
139
+ return deduplicate(suggestions);
140
+ }
141
+ generateBatchFailureSuggestions(pageStructure, failedStep) {
142
+ const suggestions = [];
143
+ suggestions.push(`Batch execution failed at step ${failedStep.stepIndex} (${failedStep.toolName})`);
144
+ if (pageStructure.modalStates.blockedBy.length > 0) {
145
+ suggestions.push("Modal dialog detected - may block subsequent operations");
146
+ }
147
+ if (failedStep.selector) {
148
+ suggestions.push(`Failed selector: ${failedStep.selector} - check element availability`);
149
+ }
150
+ suggestions.push("Consider adding wait steps between operations");
151
+ suggestions.push("Verify page state changes after each navigation step");
152
+ return suggestions;
153
+ }
154
+ }
155
+ export {
156
+ ErrorEnrichment
157
+ };