accessibility-server-mcp 1.0.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +762 -0
  3. package/config/wcag-rules.json +252 -0
  4. package/dist/index.d.ts +59 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +437 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/test-manual.d.ts +6 -0
  9. package/dist/test-manual.d.ts.map +1 -0
  10. package/dist/test-manual.js +66 -0
  11. package/dist/test-manual.js.map +1 -0
  12. package/dist/tools/accessibility-tester.d.ts +56 -0
  13. package/dist/tools/accessibility-tester.d.ts.map +1 -0
  14. package/dist/tools/accessibility-tester.js +317 -0
  15. package/dist/tools/accessibility-tester.js.map +1 -0
  16. package/dist/tools/color-contrast.d.ts +49 -0
  17. package/dist/tools/color-contrast.d.ts.map +1 -0
  18. package/dist/tools/color-contrast.js +237 -0
  19. package/dist/tools/color-contrast.js.map +1 -0
  20. package/dist/tools/wcag-validator.d.ts +43 -0
  21. package/dist/tools/wcag-validator.d.ts.map +1 -0
  22. package/dist/tools/wcag-validator.js +249 -0
  23. package/dist/tools/wcag-validator.js.map +1 -0
  24. package/dist/tools/website-accessibility-tester.d.ts +103 -0
  25. package/dist/tools/website-accessibility-tester.d.ts.map +1 -0
  26. package/dist/tools/website-accessibility-tester.js +228 -0
  27. package/dist/tools/website-accessibility-tester.js.map +1 -0
  28. package/dist/types/accessibility.d.ts +172 -0
  29. package/dist/types/accessibility.d.ts.map +1 -0
  30. package/dist/types/accessibility.js +5 -0
  31. package/dist/types/accessibility.js.map +1 -0
  32. package/dist/types/index.d.ts +6 -0
  33. package/dist/types/index.d.ts.map +1 -0
  34. package/dist/types/index.js +8 -0
  35. package/dist/types/index.js.map +1 -0
  36. package/dist/types/mcp.d.ts +70 -0
  37. package/dist/types/mcp.d.ts.map +1 -0
  38. package/dist/types/mcp.js +5 -0
  39. package/dist/types/mcp.js.map +1 -0
  40. package/dist/utils/browser-manager.d.ts +83 -0
  41. package/dist/utils/browser-manager.d.ts.map +1 -0
  42. package/dist/utils/browser-manager.js +292 -0
  43. package/dist/utils/browser-manager.js.map +1 -0
  44. package/dist/utils/index.d.ts +8 -0
  45. package/dist/utils/index.d.ts.map +1 -0
  46. package/dist/utils/index.js +8 -0
  47. package/dist/utils/index.js.map +1 -0
  48. package/dist/utils/logger.d.ts +36 -0
  49. package/dist/utils/logger.d.ts.map +1 -0
  50. package/dist/utils/logger.js +107 -0
  51. package/dist/utils/logger.js.map +1 -0
  52. package/dist/utils/report-generator.d.ts +31 -0
  53. package/dist/utils/report-generator.d.ts.map +1 -0
  54. package/dist/utils/report-generator.js +252 -0
  55. package/dist/utils/report-generator.js.map +1 -0
  56. package/dist/utils/website-crawler.d.ts +66 -0
  57. package/dist/utils/website-crawler.d.ts.map +1 -0
  58. package/dist/utils/website-crawler.js +306 -0
  59. package/dist/utils/website-crawler.js.map +1 -0
  60. package/package.json +80 -0
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Core types and interfaces for accessibility testing
3
+ */
4
+ export type WCAGLevel = 'A' | 'AA' | 'AAA';
5
+ export type WCAGVersion = '2.0' | '2.1' | '2.2';
6
+ export type ViolationSeverity = 'minor' | 'moderate' | 'serious' | 'critical';
7
+ export type AccessibilityCategory = 'color' | 'keyboard' | 'images' | 'headings' | 'landmarks' | 'forms' | 'links' | 'tables' | 'aria' | 'structure';
8
+ /**
9
+ * Represents an accessibility violation found during testing
10
+ */
11
+ export interface AccessibilityViolation {
12
+ /** Unique identifier for the violation rule */
13
+ id: string;
14
+ /** Impact level of the violation */
15
+ impact: ViolationSeverity;
16
+ /** Human-readable description of the violation */
17
+ description: string;
18
+ /** Help text explaining how to fix the violation */
19
+ help: string;
20
+ /** URL to detailed help documentation */
21
+ helpUrl: string;
22
+ /** DOM nodes where the violation was found */
23
+ nodes: ViolationNode[];
24
+ /** Tags categorizing this violation */
25
+ tags: string[];
26
+ /** WCAG conformance level this violation affects */
27
+ wcagLevel: WCAGLevel;
28
+ /** WCAG versions where this rule applies */
29
+ wcagVersions: WCAGVersion[];
30
+ }
31
+ /**
32
+ * Represents a DOM node where a violation occurred
33
+ */
34
+ export interface ViolationNode {
35
+ /** HTML source of the failing element */
36
+ html: string;
37
+ /** CSS selector path to the element */
38
+ target: string[];
39
+ /** XPath to the element (if available) */
40
+ xpath?: string;
41
+ /** Ancestry chain of the element */
42
+ ancestry?: string[];
43
+ /** Summary of why this element failed */
44
+ failureSummary?: string;
45
+ /** Additional data about the failure */
46
+ any?: Array<{
47
+ id: string;
48
+ data: Record<string, unknown>;
49
+ relatedNodes?: Array<{
50
+ html: string;
51
+ target: string[];
52
+ }>;
53
+ }>;
54
+ }
55
+ /**
56
+ * Complete results of an accessibility test
57
+ */
58
+ export interface AccessibilityTestResult {
59
+ /** URL that was tested (if applicable) */
60
+ url?: string;
61
+ /** Timestamp when the test was performed */
62
+ timestamp: string;
63
+ /** Duration of the test in milliseconds */
64
+ testDuration: number;
65
+ /** Violations found during testing */
66
+ violations: AccessibilityViolation[];
67
+ /** Rules that passed */
68
+ passes: AccessibilityRule[];
69
+ /** Summary statistics of the test */
70
+ summary: TestSummary;
71
+ /** Metadata about the test environment */
72
+ metadata: TestMetadata;
73
+ }
74
+ /**
75
+ * Represents an accessibility rule that was tested
76
+ */
77
+ export interface AccessibilityRule {
78
+ /** Unique identifier for the rule */
79
+ id: string;
80
+ /** Human-readable description of the rule */
81
+ description: string;
82
+ /** Impact level if this rule fails */
83
+ impact?: ViolationSeverity;
84
+ /** Tags categorizing this rule */
85
+ tags: string[];
86
+ /** Nodes that were tested for this rule */
87
+ nodes: ViolationNode[];
88
+ }
89
+ /**
90
+ * Summary statistics of test results
91
+ */
92
+ export interface TestSummary {
93
+ /** Total number of violations found */
94
+ totalViolations: number;
95
+ /** Violations grouped by severity */
96
+ violationsBySeverity: Record<ViolationSeverity, number>;
97
+ /** Number of rules that passed */
98
+ passedRules: number;
99
+ /** Overall accessibility score (0-100) */
100
+ score: number;
101
+ /** Compliance status for each WCAG level */
102
+ compliance: Record<WCAGLevel, boolean>;
103
+ }
104
+ /**
105
+ * Metadata about the test environment and configuration
106
+ */
107
+ export interface TestMetadata {
108
+ /** User agent string used for testing */
109
+ userAgent: string;
110
+ /** Viewport dimensions */
111
+ viewport: {
112
+ width: number;
113
+ height: number;
114
+ };
115
+ /** WCAG level tested */
116
+ wcagLevel: WCAGLevel;
117
+ }
118
+ /**
119
+ * Color contrast analysis result
120
+ */
121
+ export interface ColorContrastResult {
122
+ /** Foreground color value */
123
+ foreground: string;
124
+ /** Background color value */
125
+ background: string;
126
+ /** Calculated contrast ratio */
127
+ ratio: number;
128
+ /** WCAG AA compliance status */
129
+ wcagAA: {
130
+ /** Compliance for normal text */
131
+ normal: boolean;
132
+ /** Compliance for large text */
133
+ large: boolean;
134
+ };
135
+ /** WCAG AAA compliance status */
136
+ wcagAAA: {
137
+ /** Compliance for normal text */
138
+ normal: boolean;
139
+ /** Compliance for large text */
140
+ large: boolean;
141
+ };
142
+ /** Accessibility score for this color combination */
143
+ score: number;
144
+ /** Recommendations for improvement */
145
+ recommendations?: string[];
146
+ }
147
+ /**
148
+ * WCAG rule information
149
+ */
150
+ export interface WCAGRule {
151
+ /** Unique rule identifier */
152
+ id: string;
153
+ /** Human-readable title */
154
+ title: string;
155
+ /** Detailed description */
156
+ description: string;
157
+ /** WCAG level this rule applies to */
158
+ level: WCAGLevel;
159
+ /** WCAG versions where this rule exists */
160
+ versions: WCAGVersion[];
161
+ /** Categories this rule belongs to */
162
+ categories: AccessibilityCategory[];
163
+ /** Tags for filtering */
164
+ tags: string[];
165
+ /** Link to official WCAG documentation */
166
+ wcagUrl: string;
167
+ /** Whether this rule is automated or manual */
168
+ testType: 'automated' | 'manual' | 'semi-automated';
169
+ /** Success criteria reference */
170
+ successCriteria: string;
171
+ }
172
+ //# sourceMappingURL=accessibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility.d.ts","sourceRoot":"","sources":["../../src/types/accessibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Core types and interfaces for accessibility testing
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=accessibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility.js","sourceRoot":"","sources":["../../src/types/accessibility.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Export all types for easy importing
3
+ */
4
+ export * from './accessibility.js';
5
+ export * from './mcp.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,cAAc,oBAAoB,CAAC;AAGnC,cAAc,UAAU,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Export all types for easy importing
3
+ */
4
+ // Accessibility types
5
+ export * from './accessibility.js';
6
+ // MCP types
7
+ export * from './mcp.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,sBAAsB;AACtB,cAAc,oBAAoB,CAAC;AAEnC,YAAY;AACZ,cAAc,UAAU,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Types for MCP server inputs and outputs
3
+ */
4
+ import { WCAGLevel } from './accessibility.js';
5
+ /**
6
+ * Input parameters for accessibility testing (unified URL/HTML)
7
+ */
8
+ export interface TestAccessibilityInput {
9
+ /** Target to test - URL or HTML content */
10
+ target: string;
11
+ /** Type of target */
12
+ type: 'url' | 'html';
13
+ /** Testing depth level */
14
+ level: 'basic' | 'full';
15
+ /** WCAG compliance level to test against */
16
+ wcagLevel: WCAGLevel;
17
+ }
18
+ /**
19
+ * Input parameters for color contrast checking
20
+ */
21
+ export interface ColorContrastInput {
22
+ /** Foreground color (hex, rgb, hsl, or named color) */
23
+ foreground: string;
24
+ /** Background color (hex, rgb, hsl, or named color) */
25
+ background: string;
26
+ /** Font size in pixels */
27
+ fontSize: number;
28
+ /** Whether the text is bold */
29
+ isBold: boolean;
30
+ }
31
+ /**
32
+ * Input parameters for getting WCAG rules information
33
+ */
34
+ export interface GetWCAGRulesInput {
35
+ /** Filter by WCAG level */
36
+ wcagLevel?: WCAGLevel;
37
+ /** Filter by category */
38
+ category?: string;
39
+ /** Search term for rule titles/descriptions */
40
+ search?: string;
41
+ /** Maximum number of rules to return */
42
+ limit?: number;
43
+ }
44
+ /**
45
+ * Standard error response structure
46
+ */
47
+ export interface ErrorResponse {
48
+ /** Error type */
49
+ type: 'validation' | 'network' | 'timeout' | 'parsing' | 'internal';
50
+ /** Error message */
51
+ message: string;
52
+ /** Additional error details */
53
+ details?: Record<string, unknown>;
54
+ }
55
+ /**
56
+ * Base response wrapper for all tool outputs
57
+ */
58
+ export interface ToolResponse<T> {
59
+ /** Whether the operation was successful */
60
+ success: boolean;
61
+ /** Result data (if successful) */
62
+ data?: T;
63
+ /** Error information (if failed) */
64
+ error?: ErrorResponse;
65
+ /** Processing time in milliseconds */
66
+ processingTime: number;
67
+ /** Timestamp of the response */
68
+ timestamp: string;
69
+ }
70
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/types/mcp.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,0BAA0B;IAC1B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,4CAA4C;IAC5C,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,iBAAiB;IACjB,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IACpE,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,oCAAoC;IACpC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types for MCP server inputs and outputs
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/types/mcp.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,83 @@
1
+ import { Browser, Page, PuppeteerLaunchOptions } from 'puppeteer';
2
+ /**
3
+ * Browser manager for handling Puppeteer browser instances
4
+ * Provides connection pooling, error handling, and resource cleanup
5
+ */
6
+ export declare class BrowserManager {
7
+ private readonly logger;
8
+ private readonly browserPool;
9
+ private readonly maxBrowsers;
10
+ private isLaunching;
11
+ private readonly launchOptions;
12
+ constructor(options?: Partial<PuppeteerLaunchOptions & {
13
+ maxBrowsers?: number;
14
+ }>);
15
+ /**
16
+ * Get or create a browser instance from the pool
17
+ */
18
+ getBrowser(): Promise<Browser>;
19
+ /**
20
+ * Create a new browser and add it to the pool
21
+ */
22
+ private createNewBrowser;
23
+ /**
24
+ * Wait for browser launch to complete
25
+ */
26
+ private waitForLaunch;
27
+ /**
28
+ * Wait for an available browser in the pool
29
+ */
30
+ private waitForAvailableBrowser;
31
+ /**
32
+ * Remove disconnected browsers from pool
33
+ */
34
+ private cleanupDisconnectedBrowsers;
35
+ /**
36
+ * Remove a specific browser from the pool
37
+ */
38
+ private removeBrowserFromPool;
39
+ /**
40
+ * Create a new page with standard configuration
41
+ */
42
+ createPage(options?: {
43
+ viewport?: {
44
+ width: number;
45
+ height: number;
46
+ };
47
+ userAgent?: string;
48
+ timeout?: number;
49
+ }): Promise<Page>;
50
+ /**
51
+ * Navigate to a URL with retry logic
52
+ */
53
+ navigateToURL(page: Page, url: string, options?: {
54
+ waitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
55
+ timeout?: number;
56
+ retries?: number;
57
+ }): Promise<void>;
58
+ /**
59
+ * Close a page safely
60
+ */
61
+ closePage(page: Page): Promise<void>;
62
+ /**
63
+ * Clean up all browser resources
64
+ */
65
+ cleanup(): Promise<void>;
66
+ /**
67
+ * Get browser pool status
68
+ */
69
+ getPoolStatus(): {
70
+ total: number;
71
+ connected: number;
72
+ available: number;
73
+ };
74
+ /**
75
+ * Get browser information
76
+ */
77
+ getBrowserInfo(): Promise<{
78
+ version: string;
79
+ userAgent: string;
80
+ poolStatus: any;
81
+ } | null>;
82
+ }
83
+ //# sourceMappingURL=browser-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-manager.d.ts","sourceRoot":"","sources":["../../src/utils/browser-manager.ts"],"names":[],"mappings":"AAAA,OAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAG7E;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;gBAE3C,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAuBhF;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAwBpC;;OAEG;YACW,gBAAgB;IA8B9B;;OAEG;YACW,aAAa;IAe3B;;OAEG;YACW,uBAAuB;IAqBrC;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAcnC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;OAEG;IACG,UAAU,CAAC,OAAO,CAAC,EAAE;QACzB,QAAQ,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmDjB;;OAEG;IACG,aAAa,CACjB,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,cAAc,GAAG,cAAc,CAAC;QAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GACA,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB9B;;OAEG;IACH,aAAa,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAWxE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,GAAG,CAAA;KAAE,GAAG,IAAI,CAAC;CAkBhG"}
@@ -0,0 +1,292 @@
1
+ import puppeteer from 'puppeteer';
2
+ import { Logger } from './logger.js';
3
+ /**
4
+ * Browser manager for handling Puppeteer browser instances
5
+ * Provides connection pooling, error handling, and resource cleanup
6
+ */
7
+ export class BrowserManager {
8
+ logger;
9
+ browserPool = [];
10
+ maxBrowsers = 3;
11
+ isLaunching = false;
12
+ launchOptions;
13
+ constructor(options) {
14
+ this.logger = new Logger().child({ component: 'BrowserManager' });
15
+ this.maxBrowsers = options?.maxBrowsers ?? 3;
16
+ this.launchOptions = {
17
+ headless: 'new',
18
+ args: [
19
+ '--no-sandbox',
20
+ '--disable-setuid-sandbox',
21
+ '--disable-dev-shm-usage',
22
+ '--disable-gpu',
23
+ '--no-first-run',
24
+ '--disable-extensions',
25
+ '--disable-default-apps',
26
+ '--disable-background-timer-throttling',
27
+ '--disable-backgrounding-occluded-windows',
28
+ '--disable-renderer-backgrounding'
29
+ ],
30
+ timeout: 30000,
31
+ ...options
32
+ };
33
+ }
34
+ /**
35
+ * Get or create a browser instance from the pool
36
+ */
37
+ async getBrowser() {
38
+ // Try to find an available browser in the pool
39
+ const availableBrowser = this.browserPool.find(browser => browser.connected);
40
+ if (availableBrowser) {
41
+ this.logger.debug('Reusing browser from pool');
42
+ return availableBrowser;
43
+ }
44
+ // Remove disconnected browsers from pool
45
+ this.cleanupDisconnectedBrowsers();
46
+ // Create new browser if pool is not full
47
+ if (this.browserPool.length < this.maxBrowsers) {
48
+ return this.createNewBrowser();
49
+ }
50
+ // Wait for a browser to become available if pool is full
51
+ this.logger.warn('Browser pool is full, waiting for available browser');
52
+ return this.waitForAvailableBrowser();
53
+ }
54
+ /**
55
+ * Create a new browser and add it to the pool
56
+ */
57
+ async createNewBrowser() {
58
+ // Prevent multiple launch attempts
59
+ if (this.isLaunching) {
60
+ await this.waitForLaunch();
61
+ return this.getBrowser();
62
+ }
63
+ this.isLaunching = true;
64
+ try {
65
+ this.logger.info('Creating new browser instance');
66
+ const browser = await puppeteer.launch(this.launchOptions);
67
+ // Set up error handlers
68
+ browser.on('disconnected', () => {
69
+ this.logger.warn('Browser disconnected');
70
+ this.removeBrowserFromPool(browser);
71
+ });
72
+ this.browserPool.push(browser);
73
+ this.logger.info(`Browser created successfully. Pool size: ${this.browserPool.length}`);
74
+ return browser;
75
+ }
76
+ catch (error) {
77
+ this.logger.error('Failed to launch browser', error instanceof Error ? error : new Error(String(error)));
78
+ throw new Error(`Browser launch failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
79
+ }
80
+ finally {
81
+ this.isLaunching = false;
82
+ }
83
+ }
84
+ /**
85
+ * Wait for browser launch to complete
86
+ */
87
+ async waitForLaunch() {
88
+ const maxWait = 30000; // 30 seconds
89
+ const checkInterval = 100; // 100ms
90
+ let waited = 0;
91
+ while (this.isLaunching && waited < maxWait) {
92
+ await new Promise(resolve => setTimeout(resolve, checkInterval));
93
+ waited += checkInterval;
94
+ }
95
+ if (waited >= maxWait) {
96
+ throw new Error('Timeout waiting for browser to launch');
97
+ }
98
+ }
99
+ /**
100
+ * Wait for an available browser in the pool
101
+ */
102
+ async waitForAvailableBrowser() {
103
+ const maxWait = 10000; // 10 seconds
104
+ const checkInterval = 500; // 500ms
105
+ let waited = 0;
106
+ while (waited < maxWait) {
107
+ const availableBrowser = this.browserPool.find(browser => browser.connected);
108
+ if (availableBrowser) {
109
+ return availableBrowser;
110
+ }
111
+ await new Promise(resolve => setTimeout(resolve, checkInterval));
112
+ waited += checkInterval;
113
+ }
114
+ throw new Error('Timeout waiting for available browser in pool');
115
+ }
116
+ /**
117
+ * Remove disconnected browsers from pool
118
+ */
119
+ cleanupDisconnectedBrowsers() {
120
+ const initialSize = this.browserPool.length;
121
+ for (let i = this.browserPool.length - 1; i >= 0; i--) {
122
+ const browser = this.browserPool[i];
123
+ if (browser && !browser.connected) {
124
+ this.browserPool.splice(i, 1);
125
+ }
126
+ }
127
+ if (this.browserPool.length !== initialSize) {
128
+ this.logger.debug(`Cleaned up ${initialSize - this.browserPool.length} disconnected browsers`);
129
+ }
130
+ }
131
+ /**
132
+ * Remove a specific browser from the pool
133
+ */
134
+ removeBrowserFromPool(browser) {
135
+ const index = this.browserPool.indexOf(browser);
136
+ if (index > -1) {
137
+ this.browserPool.splice(index, 1);
138
+ this.logger.debug(`Removed browser from pool. Pool size: ${this.browserPool.length}`);
139
+ }
140
+ }
141
+ /**
142
+ * Create a new page with standard configuration
143
+ */
144
+ async createPage(options) {
145
+ const browser = await this.getBrowser();
146
+ const page = await browser.newPage();
147
+ try {
148
+ // Set viewport
149
+ if (options?.viewport) {
150
+ await page.setViewport(options.viewport);
151
+ }
152
+ else {
153
+ await page.setViewport({ width: 1920, height: 1080 });
154
+ }
155
+ // Set user agent if provided
156
+ if (options?.userAgent) {
157
+ await page.setUserAgent(options.userAgent);
158
+ }
159
+ // Set timeouts
160
+ const timeout = options?.timeout ?? 30000;
161
+ page.setDefaultTimeout(timeout);
162
+ page.setDefaultNavigationTimeout(timeout);
163
+ // Block only heavy media to speed up loading but keep essential resources
164
+ await page.setRequestInterception(true);
165
+ page.on('request', (req) => {
166
+ const resourceType = req.resourceType();
167
+ // Only block heavy media files, keep CSS and fonts for proper accessibility testing
168
+ if (['image', 'media'].includes(resourceType)) {
169
+ req.abort();
170
+ }
171
+ else {
172
+ req.continue();
173
+ }
174
+ });
175
+ // Add error handling
176
+ page.on('error', (error) => {
177
+ this.logger.error('Page error occurred', error);
178
+ });
179
+ page.on('pageerror', (error) => {
180
+ this.logger.error('Page script error', error);
181
+ });
182
+ this.logger.debug('Created new page');
183
+ return page;
184
+ }
185
+ catch (error) {
186
+ await page.close();
187
+ throw error;
188
+ }
189
+ }
190
+ /**
191
+ * Navigate to a URL with retry logic
192
+ */
193
+ async navigateToURL(page, url, options) {
194
+ const maxRetries = options?.retries ?? 3;
195
+ const waitUntil = options?.waitUntil ?? 'networkidle2';
196
+ const timeout = options?.timeout ?? 30000;
197
+ let lastError = null;
198
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
199
+ try {
200
+ this.logger.debug(`Navigating to URL (attempt ${attempt}/${maxRetries})`, { url });
201
+ await page.goto(url, {
202
+ waitUntil,
203
+ timeout
204
+ });
205
+ this.logger.debug('Successfully navigated to URL', { url });
206
+ return;
207
+ }
208
+ catch (error) {
209
+ lastError = error instanceof Error ? error : new Error(String(error));
210
+ this.logger.warn(`Navigation attempt ${attempt} failed`, {
211
+ url,
212
+ error: lastError.message,
213
+ attempt,
214
+ maxRetries
215
+ });
216
+ if (attempt < maxRetries) {
217
+ // Wait before retrying
218
+ await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
219
+ }
220
+ }
221
+ }
222
+ throw new Error(`Failed to navigate to ${url} after ${maxRetries} attempts: ${lastError?.message ?? 'Unknown error'}`);
223
+ }
224
+ /**
225
+ * Close a page safely
226
+ */
227
+ async closePage(page) {
228
+ try {
229
+ if (!page.isClosed()) {
230
+ await page.close();
231
+ this.logger.debug('Page closed successfully');
232
+ }
233
+ }
234
+ catch (error) {
235
+ this.logger.warn('Error closing page', { error: String(error) });
236
+ }
237
+ }
238
+ /**
239
+ * Clean up all browser resources
240
+ */
241
+ async cleanup() {
242
+ this.logger.info(`Cleaning up ${this.browserPool.length} browsers`);
243
+ const cleanupPromises = this.browserPool.map(async (browser, index) => {
244
+ try {
245
+ if (browser.connected) {
246
+ await browser.close();
247
+ this.logger.debug(`Browser ${index} closed successfully`);
248
+ }
249
+ }
250
+ catch (error) {
251
+ this.logger.warn(`Error closing browser ${index}`, { error: String(error) });
252
+ }
253
+ });
254
+ await Promise.allSettled(cleanupPromises);
255
+ this.browserPool.length = 0; // Clear the pool
256
+ this.logger.info('All browsers cleaned up');
257
+ }
258
+ /**
259
+ * Get browser pool status
260
+ */
261
+ getPoolStatus() {
262
+ const total = this.browserPool.length;
263
+ const connected = this.browserPool.filter(browser => browser.connected).length;
264
+ return {
265
+ total,
266
+ connected,
267
+ available: this.maxBrowsers - total
268
+ };
269
+ }
270
+ /**
271
+ * Get browser information
272
+ */
273
+ async getBrowserInfo() {
274
+ try {
275
+ const browser = await this.getBrowser();
276
+ const version = await browser.version();
277
+ const page = await this.createPage();
278
+ const userAgent = await page.evaluate(() => navigator.userAgent);
279
+ await this.closePage(page);
280
+ return {
281
+ version,
282
+ userAgent,
283
+ poolStatus: this.getPoolStatus()
284
+ };
285
+ }
286
+ catch (error) {
287
+ this.logger.error('Failed to get browser info', error instanceof Error ? error : new Error(String(error)));
288
+ return null;
289
+ }
290
+ }
291
+ }
292
+ //# sourceMappingURL=browser-manager.js.map