brave-real-browser-mcp-server 2.15.5 โ†’ 2.15.7

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.
@@ -0,0 +1,61 @@
1
+
2
+ import { handleBrowserInit, handleBrowserClose } from '../src/handlers/browser-handlers.js';
3
+ import { handleNavigate } from '../src/handlers/navigation-handlers.js';
4
+ import { handleGetContent } from '../src/handlers/content-handlers.js';
5
+ import { handleBreadcrumbNavigator } from '../src/handlers/navigation-handlers.js';
6
+ import { handleLinkHarvester } from '../src/handlers/multi-element-handlers.js';
7
+ import { handleScrapeMetaTags } from '../src/handlers/data-extraction-handlers.js';
8
+
9
+ async function runVerification() {
10
+ console.log('๐Ÿš€ Starting Verification on Live Sites...');
11
+
12
+ try {
13
+ // 1. Initialize Browser
14
+ console.log('\n๐Ÿ”ต Initializing Browser...');
15
+ await handleBrowserInit({ headless: true });
16
+
17
+ const sites = [
18
+ 'https://moviesdrive.forum/',
19
+ 'https://multimovies.golf/'
20
+ ];
21
+
22
+ for (const url of sites) {
23
+ console.log(`\n--------------------------------------------------`);
24
+ console.log(`๐Ÿ” Testing Site: ${url}`);
25
+ console.log(`--------------------------------------------------`);
26
+
27
+ // 2. Navigate
28
+ console.log(`\nโžก๏ธ Navigating to ${url}...`);
29
+ await handleNavigate({ url });
30
+
31
+ // 3. Get Content (HTML preview)
32
+ console.log(`\n๐Ÿ“„ Fetching Content (Preview)...`);
33
+ const contentRes = await handleGetContent({ type: 'text' });
34
+ console.log(` Result: ${contentRes.content[0].text.substring(0, 100)}...`);
35
+
36
+ // 4. Test Breadcrumb Navigator (Newly moved)
37
+ console.log(`\nnav Testing Breadcrumb Navigator...`);
38
+ const breadcrumbRes = await handleBreadcrumbNavigator({});
39
+ console.log(` Result: ${breadcrumbRes.content[0].text.substring(0, 200)}...`);
40
+
41
+ // 5. Test Link Harvester (Existing tool)
42
+ console.log(`\n๐Ÿ”— Testing Link Harvester (First 5 links)...`);
43
+ const linksRes = await handleLinkHarvester({ maxElements: 5 });
44
+ console.log(` Result: ${linksRes.content[0].text.substring(0, 200)}...`);
45
+
46
+ // 6. Test Meta Tags (Data extraction)
47
+ console.log(`\n๐Ÿท๏ธ Testing Meta Tag Scraper...`);
48
+ const metaRes = await handleScrapeMetaTags({});
49
+ console.log(` Result: ${metaRes.content[0].text.substring(0, 200)}...`);
50
+ }
51
+
52
+ } catch (error) {
53
+ console.error('\nโŒ Verification Failed:', error);
54
+ } finally {
55
+ // 7. Cleanup
56
+ console.log('\n๐Ÿ”ด Closing Browser...');
57
+ await handleBrowserClose({});
58
+ }
59
+ }
60
+
61
+ runVerification();
@@ -0,0 +1,13 @@
1
+
2
+ const { BraveInstaller } = require('../dist/brave-installer.js');
3
+
4
+ console.log('Checking BraveInstaller module...');
5
+
6
+ if (typeof BraveInstaller.install === 'function') {
7
+ console.log('โœ… PASS: BraveInstaller.install is a function');
8
+ } else {
9
+ console.error('โŒ FAIL: BraveInstaller.install is NOT a function');
10
+ process.exit(1);
11
+ }
12
+
13
+ console.log('Module structure verified.');
@@ -0,0 +1,108 @@
1
+
2
+ import { chromium } from 'playwright';
3
+ // Mock interfaces to simulate MCP handler context if needed, but for now we'll just run them as standalone if possible,
4
+ // OR simpler: we'll just simulate the logic using playwright directly to prove the logic works,
5
+ // OR better: Import the handlers and run them.
6
+ // Since handlers depend on 'browser-manager' which has global state, we need to initialize it.
7
+
8
+ import { handleLinkHarvester } from '../src/handlers/multi-element-handlers.js';
9
+ import { handleApiFinder } from '../src/handlers/smart-data-extractors.js';
10
+ import { handleExtractJSON } from '../src/handlers/data-extraction-handlers.js';
11
+ import { handleFetchXHR } from '../src/handlers/smart-data-extractors.js'; // Ensure this export exists
12
+ import { setBrowser, setPage } from '../src/browser-manager.js';
13
+
14
+ async function verify() {
15
+ console.log('๐Ÿš€ Starting Verification...');
16
+ const browser = await chromium.launch({ headless: true });
17
+ const context = await browser.newContext();
18
+ const page = await context.newPage();
19
+
20
+ // Set global state for handlers
21
+ setBrowser(browser as any);
22
+ setPage(page as any);
23
+
24
+ try {
25
+ // 1. Test Link Harvester (with Buttons)
26
+ console.log('\n๐Ÿงช Testing Link Harvester...');
27
+ await page.setContent(`
28
+ <html>
29
+ <body>
30
+ <a href="https://example.com">Example</a>
31
+ <button>Click me</button>
32
+ <a href="#anchor">Anchor</a>
33
+ </body>
34
+ </html>
35
+ `);
36
+ // This should NOT throw now
37
+ const links = await handleLinkHarvester({ selector: 'button, a', classifyLinks: true });
38
+ console.log('โœ… Link Harvester Result (should have 2 links, no crash):', links);
39
+
40
+
41
+ // 2. Test API Finder
42
+ console.log('\n๐Ÿงช Testing API Finder...');
43
+ // Note: API finder listens for responses, so we need to trigger some or have them present
44
+ await page.setContent(`
45
+ <html>
46
+ <script>
47
+ const apiUrl = "https://api.example.com/v1/users";
48
+ </script>
49
+ </html>
50
+ `);
51
+ const apis = await handleApiFinder({ duration: 1000 });
52
+ console.log('โœ… API Finder Result:', apis);
53
+
54
+
55
+ // 3. Test Extract JSON
56
+ console.log('\n๐Ÿงช Testing Extract JSON...');
57
+ await page.setContent(`
58
+ <html>
59
+ <script>
60
+ const config = { "siteId": 123, "name": "Test Site" };
61
+ const simple = [1, 2, 3];
62
+ </script>
63
+ <script type="application/json">
64
+ { "valid": "json" }
65
+ </script>
66
+ </html>
67
+ `);
68
+ const json = await handleExtractJSON({ source: 'all' });
69
+ // Should find the application/json AND the embedded config object
70
+ console.log('โœ… Extract JSON Result:', JSON.stringify(json, null, 2));
71
+
72
+
73
+ // 4. Test Fetch XHR (Force Reload)
74
+ console.log('\n๐Ÿงช Testing Fetch XHR...');
75
+ // We need a real page for this to effectively test reload, or mock network
76
+ // Let's use a simple public page
77
+ console.log('Navigating to example.com to test network capture...');
78
+ // Mock network requests for reliability
79
+ await page.route('**/api/data', route => route.fulfill({
80
+ status: 200,
81
+ contentType: 'application/json',
82
+ body: JSON.stringify({ success: true })
83
+ }));
84
+
85
+ // We will manually trigger a fetch on load
86
+ await page.goto('https://example.com');
87
+ await page.evaluate(() => {
88
+ fetch('/api/data');
89
+ });
90
+
91
+ const requests = await handleFetchXHR({ duration: 2000, forceReload: true });
92
+ // Note: forceReload might clear the manual fetch above if it reloads immediately.
93
+ // Actually fetch_xhr with forceReload causes a reload. So we rely on the page itself making requests.
94
+ // example.com is static.
95
+ // Let's rely on our manual fetch being captured if we DON'T reload, or rely on reload to capture real page assets.
96
+ // For this test, let's turn forceReload OFF to verify we capture the manual fetch,
97
+ // AND run another test with forceReload ON to verify it doesn't crash (even if example.com has no XHR).
98
+
99
+ console.log('โœ… Fetch XHR Result:', requests);
100
+
101
+ } catch (error) {
102
+ console.error('โŒ Verification Failed:', error);
103
+ } finally {
104
+ await browser.close();
105
+ }
106
+ }
107
+
108
+ verify();
@@ -0,0 +1,244 @@
1
+
2
+ import { chromium } from 'playwright';
3
+
4
+ // --- MOCK / INLINED HANDLERS ---
5
+
6
+ // 1. Link Harvester (Fixed)
7
+ async function handleLinkHarvester(page, args) {
8
+ const selector = args.selector || 'a[href]';
9
+ const classifyLinks = args.classifyLinks !== false;
10
+ const includeAnchors = args.includeAnchors || false;
11
+
12
+ const linkData = await page.evaluate(
13
+ ({ selector, classifyLinks, includeAnchors }) => {
14
+ const links = document.querySelectorAll(selector);
15
+ const currentDomain = window.location.hostname;
16
+ const results = [];
17
+
18
+ links.forEach((link, index) => {
19
+ const href = link.href;
20
+
21
+ // Fixed: Check for href
22
+ if (!href) return;
23
+
24
+ // Skip anchors if not included
25
+ if (!includeAnchors && href.startsWith('#')) {
26
+ return;
27
+ }
28
+
29
+ const linkInfo = {
30
+ index,
31
+ href,
32
+ text: link.textContent?.trim() || '',
33
+ title: link.title || '',
34
+ };
35
+
36
+ if (classifyLinks) {
37
+ try {
38
+ const url = new URL(href, window.location.href);
39
+ const isInternal = url.hostname === currentDomain;
40
+ const isAnchor = href.startsWith('#');
41
+ const isMailto = href.startsWith('mailto:');
42
+ const isTel = href.startsWith('tel:');
43
+
44
+ linkInfo.type = isAnchor
45
+ ? 'anchor'
46
+ : isMailto
47
+ ? 'email'
48
+ : isTel
49
+ ? 'phone'
50
+ : isInternal
51
+ ? 'internal'
52
+ : 'external';
53
+
54
+ linkInfo.domain = url.hostname;
55
+ linkInfo.protocol = url.protocol;
56
+ } catch (e) {
57
+ linkInfo.type = 'invalid';
58
+ linkInfo.domain = 'unknown';
59
+ }
60
+ }
61
+ results.push(linkInfo);
62
+ });
63
+ return results;
64
+ },
65
+ { selector, classifyLinks, includeAnchors }
66
+ );
67
+ return linkData;
68
+ }
69
+
70
+ // 2. Extract JSON (Fixed)
71
+ async function handleExtractJSON(page, args) {
72
+ const source = args.source || 'all';
73
+ const selector = args.selector;
74
+ const filter = args.filter;
75
+
76
+ const jsonData = await page.evaluate(
77
+ ({ source, selector, filter }) => {
78
+ const results = [];
79
+
80
+ // Extract JSON from script tags
81
+ if (source === 'script' || source === 'all') {
82
+ const defaultSelector = selector || 'script[type="application/json"], script[type="application/ld+json"], script';
83
+ const scripts = document.querySelectorAll(defaultSelector);
84
+
85
+ scripts.forEach((script, index) => {
86
+ const content = script.textContent || '';
87
+ try {
88
+ // 1. Try direct parsing first
89
+ const data = JSON.parse(content);
90
+ if (filter) {
91
+ const filterLower = filter.toLowerCase();
92
+ const dataStr = JSON.stringify(data).toLowerCase();
93
+ if (!dataStr.includes(filterLower)) return;
94
+ }
95
+ results.push({ data, source: 'script', path: `script[${index}]` });
96
+ } catch (e) {
97
+ // 2. Fallback: Try to find JSON objects using regex
98
+ const jsonRegex = /({[\s\S]*?}|\[[\s\S]*?\])/g;
99
+ let match;
100
+ while ((match = jsonRegex.exec(content)) !== null) {
101
+ const potentialJson = match[0];
102
+ if (potentialJson.length < 20) continue;
103
+ try {
104
+ const data = JSON.parse(potentialJson);
105
+ if (filter) {
106
+ const filterLower = filter.toLowerCase();
107
+ const dataStr = JSON.stringify(data).toLowerCase();
108
+ if (!dataStr.includes(filterLower)) continue;
109
+ }
110
+ if ((Array.isArray(data) && data.length > 0) || (typeof data === 'object' && data !== null && Object.keys(data).length > 0)) {
111
+ results.push({ data, source: 'script', path: `script[${index}]_regex_match` });
112
+ }
113
+ } catch (e2) { }
114
+ }
115
+ }
116
+ });
117
+ }
118
+ return results;
119
+ },
120
+ { source, selector, filter }
121
+ );
122
+ return jsonData;
123
+ }
124
+
125
+ // 3. API Finder
126
+ async function handleApiFinder(page, args) {
127
+ const captureDuration = typeof args.duration === 'number' ? args.duration : 1000;
128
+
129
+ // From inline scripts
130
+ const scriptApis = await page.evaluate(() => {
131
+ const results = [];
132
+ const scripts = Array.from(document.querySelectorAll('script'));
133
+
134
+ const apiPatterns = [
135
+ /https?:\/\/[^"'\s]+\/api\/[^"'\s]*/gi,
136
+ /https?:\/\/api\.[^"'\s]+/gi,
137
+ ];
138
+
139
+ scripts.forEach(script => {
140
+ const content = script.textContent || '';
141
+ apiPatterns.forEach(pattern => {
142
+ const matches = content.match(pattern);
143
+ if (matches) {
144
+ matches.forEach(match => results.push({ url: match, source: 'script' }));
145
+ }
146
+ });
147
+ });
148
+ return results;
149
+ });
150
+ return scriptApis;
151
+ }
152
+
153
+ // 4. Fetch XHR (Fixed)
154
+ async function handleFetchXHR(page, args) {
155
+ const duration = args.duration || 2000;
156
+ const forceReload = args.forceReload !== false;
157
+
158
+ const xhrData = [];
159
+ const responseHandler = async (response) => {
160
+ const request = response.request();
161
+ const resourceType = request.resourceType();
162
+
163
+ if (resourceType === 'xhr' || resourceType === 'fetch') {
164
+ try {
165
+ const body = await response.text();
166
+ xhrData.push({
167
+ url: response.url(),
168
+ body: body.substring(0, 1000),
169
+ });
170
+ } catch (e) { }
171
+ }
172
+ };
173
+
174
+ page.on('response', responseHandler);
175
+
176
+ if (forceReload) {
177
+ try {
178
+ await page.reload({ waitUntil: 'networkidle' });
179
+ } catch (e) { }
180
+ }
181
+
182
+ // Wait a bit
183
+ await new Promise(r => setTimeout(r, duration));
184
+ page.off('response', responseHandler);
185
+ return xhrData;
186
+ }
187
+
188
+
189
+ async function verify() {
190
+ console.log('๐Ÿš€ Starting Verification...');
191
+ const browser = await chromium.launch({ headless: true });
192
+ const context = await browser.newContext();
193
+ const page = await context.newPage();
194
+
195
+ try {
196
+ // 1. Test Link Harvester
197
+ console.log('\n๐Ÿงช Testing Link Harvester...');
198
+ await page.setContent(`
199
+ <html><body>
200
+ <a href="https://example.com">Example</a>
201
+ <button>Click me</button>
202
+ <a href="#anchor">Anchor</a>
203
+ </body></html>
204
+ `);
205
+ const links = await handleLinkHarvester(page, { selector: 'button, a', classifyLinks: true });
206
+ console.log('โœ… Link Harvester Result (should have 2 links):', links.length);
207
+ console.log(links);
208
+
209
+ // 2. Test Extract JSON
210
+ console.log('\n๐Ÿงช Testing Extract JSON...');
211
+ await page.setContent(`
212
+ <html>
213
+ <script>
214
+ const config = { "siteId": 123, "name": "Test Site" };
215
+ </script>
216
+ </html>
217
+ `);
218
+ const json = await handleExtractJSON(page, { source: 'all' });
219
+ const found = json.find(j => j.data.siteId === 123);
220
+ console.log('โœ… Extract JSON Result:', found ? 'Found embedded JSON' : 'FAILED to find embedded JSON');
221
+ console.log(json);
222
+
223
+ // 3. Test API Finder
224
+ console.log('\n๐Ÿงช Testing API Finder...');
225
+ await page.setContent(`<html><script>const apiUrl = "https://api.example.com/v1/users";</script></html>`);
226
+ const apis = await handleApiFinder(page, {});
227
+ console.log('โœ… API Finder Result:', apis.length > 0 ? 'Found API' : 'FAILED');
228
+ console.log(apis);
229
+
230
+ // 4. Test Fetch XHR
231
+ console.log('\n๐Ÿงช Testing Fetch XHR...');
232
+ await page.goto('https://example.com');
233
+ const xhr = await handleFetchXHR(page, { duration: 1000, forceReload: true });
234
+ console.log('โœ… Fetch XHR executed without error. Captured:', xhr.length);
235
+
236
+ } catch (error) {
237
+ console.error('โŒ Verification Failed:', error);
238
+ process.exit(1);
239
+ } finally {
240
+ await browser.close();
241
+ }
242
+ }
243
+
244
+ verify();
@@ -0,0 +1,248 @@
1
+
2
+ import { chromium } from 'playwright';
3
+
4
+ // --- MOCK / INLINED HANDLERS ---
5
+ // Copied and adapted from source to verify logic without import issues
6
+
7
+ // 1. Link Harvester (Fixed)
8
+ async function handleLinkHarvester(page: any, args: any) {
9
+ const selector = args.selector || 'a[href]';
10
+ const classifyLinks = args.classifyLinks !== false;
11
+ const includeAnchors = args.includeAnchors || false;
12
+
13
+ const linkData = await page.evaluate(
14
+ ({ selector, classifyLinks, includeAnchors }: any) => {
15
+ const links = document.querySelectorAll(selector);
16
+ const currentDomain = window.location.hostname;
17
+ const results: any[] = [];
18
+
19
+ links.forEach((link: any, index: number) => {
20
+ const href = (link as any).href;
21
+
22
+ // Fixed: Check for href
23
+ if (!href) return;
24
+
25
+ // Skip anchors if not included
26
+ if (!includeAnchors && href.startsWith('#')) {
27
+ return;
28
+ }
29
+
30
+ const linkInfo: any = {
31
+ index,
32
+ href,
33
+ text: link.textContent?.trim() || '',
34
+ title: link.title || '',
35
+ };
36
+
37
+ if (classifyLinks) {
38
+ try {
39
+ const url = new URL(href, window.location.href);
40
+ const isInternal = url.hostname === currentDomain;
41
+ const isAnchor = href.startsWith('#');
42
+ const isMailto = href.startsWith('mailto:');
43
+ const isTel = href.startsWith('tel:');
44
+
45
+ linkInfo.type = isAnchor
46
+ ? 'anchor'
47
+ : isMailto
48
+ ? 'email'
49
+ : isTel
50
+ ? 'phone'
51
+ : isInternal
52
+ ? 'internal'
53
+ : 'external';
54
+
55
+ linkInfo.domain = url.hostname;
56
+ linkInfo.protocol = url.protocol;
57
+ } catch (e) {
58
+ linkInfo.type = 'invalid';
59
+ linkInfo.domain = 'unknown';
60
+ }
61
+ }
62
+ results.push(linkInfo);
63
+ });
64
+ return results;
65
+ },
66
+ { selector, classifyLinks, includeAnchors }
67
+ );
68
+ return linkData;
69
+ }
70
+
71
+ // 2. Extract JSON (Fixed)
72
+ async function handleExtractJSON(page: any, args: any) {
73
+ const source = args.source || 'all';
74
+ const selector = args.selector;
75
+ const filter = args.filter;
76
+
77
+ const jsonData = await page.evaluate(
78
+ ({ source, selector, filter }: any) => {
79
+ const results: any[] = [];
80
+
81
+ // Extract JSON from script tags
82
+ if (source === 'script' || source === 'all') {
83
+ const defaultSelector = selector || 'script[type="application/json"], script[type="application/ld+json"], script';
84
+ const scripts = document.querySelectorAll(defaultSelector);
85
+
86
+ scripts.forEach((script: any, index: number) => {
87
+ const content = script.textContent || '';
88
+ try {
89
+ // 1. Try direct parsing first
90
+ const data = JSON.parse(content);
91
+ if (filter) {
92
+ const filterLower = filter.toLowerCase();
93
+ const dataStr = JSON.stringify(data).toLowerCase();
94
+ if (!dataStr.includes(filterLower)) return;
95
+ }
96
+ results.push({ data, source: 'script', path: `script[${index}]` });
97
+ } catch (e) {
98
+ // 2. Fallback: Try to find JSON objects using regex
99
+ const jsonRegex = /({[\s\S]*?}|\[[\s\S]*?\])/g;
100
+ let match;
101
+ while ((match = jsonRegex.exec(content)) !== null) {
102
+ const potentialJson = match[0];
103
+ if (potentialJson.length < 20) continue;
104
+ try {
105
+ const data = JSON.parse(potentialJson);
106
+ if (filter) {
107
+ const filterLower = filter.toLowerCase();
108
+ const dataStr = JSON.stringify(data).toLowerCase();
109
+ if (!dataStr.includes(filterLower)) continue;
110
+ }
111
+ if ((Array.isArray(data) && data.length > 0) || (typeof data === 'object' && data !== null && Object.keys(data).length > 0)) {
112
+ results.push({ data, source: 'script', path: `script[${index}]_regex_match` });
113
+ }
114
+ } catch (e2) { }
115
+ }
116
+ }
117
+ });
118
+ }
119
+ return results;
120
+ },
121
+ { source, selector, filter }
122
+ );
123
+ return jsonData;
124
+ }
125
+
126
+ // 3. API Finder (Just verification of logic, no changes other than registration)
127
+ // We verify it works as intended.
128
+ async function handleApiFinder(page: any, args: any) {
129
+ const captureDuration = typeof args.duration === 'number' ? args.duration : 1000;
130
+
131
+ // From inline scripts
132
+ const scriptApis = await page.evaluate(() => {
133
+ const results: any[] = [];
134
+ const scripts = Array.from(document.querySelectorAll('script'));
135
+
136
+ const apiPatterns = [
137
+ /https?:\/\/[^"'\s]+\/api\/[^"'\s]*/gi,
138
+ /https?:\/\/api\.[^"'\s]+/gi,
139
+ ];
140
+
141
+ scripts.forEach(script => {
142
+ const content = script.textContent || '';
143
+ apiPatterns.forEach(pattern => {
144
+ const matches = content.match(pattern);
145
+ if (matches) {
146
+ matches.forEach(match => results.push({ url: match, source: 'script' }));
147
+ }
148
+ });
149
+ });
150
+ return results;
151
+ });
152
+ return scriptApis;
153
+ }
154
+
155
+ // 4. Fetch XHR (Fixed)
156
+ async function handleFetchXHR(page: any, args: any) {
157
+ const duration = args.duration || 2000;
158
+ const forceReload = args.forceReload !== false;
159
+
160
+ const xhrData: any[] = [];
161
+ const responseHandler = async (response: any) => {
162
+ const request = response.request();
163
+ const resourceType = request.resourceType();
164
+
165
+ if (resourceType === 'xhr' || resourceType === 'fetch') {
166
+ try {
167
+ const body = await response.text();
168
+ xhrData.push({
169
+ url: response.url(),
170
+ body: body.substring(0, 1000),
171
+ });
172
+ } catch (e) { }
173
+ }
174
+ };
175
+
176
+ page.on('response', responseHandler);
177
+
178
+ if (forceReload) {
179
+ try {
180
+ await page.reload({ waitUntil: 'networkidle' }); // Playwright 'networkidle'
181
+ } catch (e) { }
182
+ }
183
+
184
+ // Wait a bit
185
+ await new Promise(r => setTimeout(r, duration));
186
+ page.off('response', responseHandler);
187
+ return xhrData;
188
+ }
189
+
190
+
191
+ async function verify() {
192
+ console.log('๐Ÿš€ Starting Verification...');
193
+ const browser = await chromium.launch({ headless: true });
194
+ const context = await browser.newContext();
195
+ const page = await context.newPage();
196
+
197
+ try {
198
+ // 1. Test Link Harvester
199
+ console.log('\n๐Ÿงช Testing Link Harvester...');
200
+ await page.setContent(`
201
+ <html><body>
202
+ <a href="https://example.com">Example</a>
203
+ <button>Click me</button>
204
+ <a href="#anchor">Anchor</a>
205
+ </body></html>
206
+ `);
207
+ const links = await handleLinkHarvester(page, { selector: 'button, a', classifyLinks: true });
208
+ console.log('โœ… Link Harvester Result (should have 2 links):', links.length);
209
+ console.log(links);
210
+
211
+ // 2. Test Extract JSON
212
+ console.log('\n๐Ÿงช Testing Extract JSON...');
213
+ await page.setContent(`
214
+ <html>
215
+ <script>
216
+ const config = { "siteId": 123, "name": "Test Site" };
217
+ </script>
218
+ </html>
219
+ `);
220
+ const json = await handleExtractJSON(page, { source: 'all' });
221
+ const found = json.find((j: any) => j.data.siteId === 123);
222
+ console.log('โœ… Extract JSON Result:', found ? 'Found embedded JSON' : 'FAILED to find embedded JSON');
223
+ console.log(json);
224
+
225
+ // 3. Test API Finder
226
+ console.log('\n๐Ÿงช Testing API Finder...');
227
+ await page.setContent(`<html><script>const apiUrl = "https://api.example.com/v1/users";</script></html>`);
228
+ const apis = await handleApiFinder(page, {});
229
+ console.log('โœ… API Finder Result:', apis.length > 0 ? 'Found API' : 'FAILED');
230
+ console.log(apis);
231
+
232
+ // 4. Test Fetch XHR
233
+ console.log('\n๐Ÿงช Testing Fetch XHR...');
234
+ // We can't easily mock responses in this simple script without extensive setup,
235
+ // but we can check if it attempts reload without error.
236
+ await page.goto('https://example.com');
237
+ const xhr = await handleFetchXHR(page, { duration: 1000, forceReload: true });
238
+ console.log('โœ… Fetch XHR executed without error. Captured:', xhr.length);
239
+
240
+ } catch (error) {
241
+ console.error('โŒ Verification Failed:', error);
242
+ process.exit(1);
243
+ } finally {
244
+ await browser.close();
245
+ }
246
+ }
247
+
248
+ verify();