@mcp-b/smart-dom-reader 2.2.0 → 2.3.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.
package/README.md CHANGED
@@ -14,14 +14,14 @@
14
14
 
15
15
  ## Why Use @mcp-b/smart-dom-reader?
16
16
 
17
- | Feature | Benefit |
18
- |---------|---------|
19
- | **Token-Efficient** | Progressive extraction minimizes LLM context window usage |
20
- | **Stable Selectors** | Ranked CSS selectors (ID > data-testid > ARIA > classes) for reliable automation |
21
- | **AI-Optimized Output** | Structured data designed for LLM understanding |
22
- | **Zero Dependencies** | Lightweight, runs in any browser environment |
23
- | **Shadow DOM Support** | Traverses shadow roots and iframes |
24
- | **Stateless API** | Works with any document context - Puppeteer, Playwright, browser extensions |
17
+ | Feature | Benefit |
18
+ | ----------------------- | -------------------------------------------------------------------------------- |
19
+ | **Token-Efficient** | Progressive extraction minimizes LLM context window usage |
20
+ | **Stable Selectors** | Ranked CSS selectors (ID > data-testid > ARIA > classes) for reliable automation |
21
+ | **AI-Optimized Output** | Structured data designed for LLM understanding |
22
+ | **Zero Dependencies** | Lightweight, runs in any browser environment |
23
+ | **Shadow DOM Support** | Traverses shadow roots and iframes |
24
+ | **Stateless API** | Works with any document context - Puppeteer, Playwright, browser extensions |
25
25
 
26
26
  ## Use Cases
27
27
 
@@ -69,7 +69,7 @@ const fullData = SmartDOMReader.extractFull(doc);
69
69
  const customData = SmartDOMReader.extractInteractive(doc, {
70
70
  mainContentOnly: true,
71
71
  viewportOnly: true,
72
- includeHidden: false
72
+ includeHidden: false,
73
73
  });
74
74
  ```
75
75
 
@@ -95,11 +95,10 @@ const mainContent = ProgressiveExtractor.extractRegion(
95
95
  );
96
96
 
97
97
  // Step 3: Extract readable content from a region
98
- const articleText = ProgressiveExtractor.extractContent(
99
- 'article.main-article',
100
- document,
101
- { includeHeadings: true, includeLists: true }
102
- );
98
+ const articleText = ProgressiveExtractor.extractContent('article.main-article', document, {
99
+ includeHeadings: true,
100
+ includeLists: true,
101
+ });
103
102
 
104
103
  // Structure scoped to a container (e.g., navigation only)
105
104
  const nav = document.querySelector('nav');
@@ -112,7 +111,9 @@ if (nav) {
112
111
  ## Extraction Modes
113
112
 
114
113
  ### Interactive Mode
114
+
115
115
  Focuses on elements users can interact with:
116
+
116
117
  - Buttons and button-like elements
117
118
  - Links
118
119
  - Form inputs (text, select, textarea)
@@ -120,7 +121,9 @@ Focuses on elements users can interact with:
120
121
  - Form structures and associations
121
122
 
122
123
  ### Full Mode
124
+
123
125
  Includes everything from interactive mode plus:
126
+
124
127
  - Semantic HTML elements (articles, sections, nav)
125
128
  - Headings hierarchy
126
129
  - Images with alt text
@@ -136,7 +139,7 @@ Includes everything from interactive mode plus:
136
139
  const reader = new SmartDOMReader({
137
140
  mode: 'interactive',
138
141
  mainContentOnly: true,
139
- viewportOnly: false
142
+ viewportOnly: false,
140
143
  });
141
144
  const result = reader.extract(document);
142
145
 
@@ -154,19 +157,11 @@ const overview = ProgressiveExtractor.extractStructure(document);
154
157
  // Returns: regions, forms, summary, suggestions
155
158
 
156
159
  // Step 2: Region extraction
157
- const region = ProgressiveExtractor.extractRegion(
158
- selector,
159
- document,
160
- options
161
- );
160
+ const region = ProgressiveExtractor.extractRegion(selector, document, options);
162
161
  // Returns: Full SmartDOMResult for that region
163
162
 
164
163
  // Step 3: Content extraction
165
- const content = ProgressiveExtractor.extractContent(
166
- selector,
167
- document,
168
- { includeMedia: true }
169
- );
164
+ const content = ProgressiveExtractor.extractContent(selector, document, { includeMedia: true });
170
165
  // Returns: Text content, headings, lists, tables, media
171
166
  ```
172
167
 
@@ -178,7 +173,7 @@ Both approaches return structured data optimized for AI processing:
178
173
  interface SmartDOMResult {
179
174
  mode: 'interactive' | 'full';
180
175
  timestamp: number;
181
-
176
+
182
177
  page: {
183
178
  url: string;
184
179
  title: string;
@@ -187,7 +182,7 @@ interface SmartDOMResult {
187
182
  hasModals: boolean;
188
183
  hasFocus?: string;
189
184
  };
190
-
185
+
191
186
  landmarks: {
192
187
  navigation: string[];
193
188
  main: string[];
@@ -197,7 +192,7 @@ interface SmartDOMResult {
197
192
  articles: string[];
198
193
  sections: string[];
199
194
  };
200
-
195
+
201
196
  interactive: {
202
197
  buttons: ExtractedElement[];
203
198
  links: ExtractedElement[];
@@ -205,16 +200,18 @@ interface SmartDOMResult {
205
200
  forms: FormInfo[];
206
201
  clickable: ExtractedElement[];
207
202
  };
208
-
209
- semantic?: { // Only in full mode
203
+
204
+ semantic?: {
205
+ // Only in full mode
210
206
  headings: ExtractedElement[];
211
207
  images: ExtractedElement[];
212
208
  tables: ExtractedElement[];
213
209
  lists: ExtractedElement[];
214
210
  articles: ExtractedElement[];
215
211
  };
216
-
217
- metadata?: { // Only in full mode
212
+
213
+ metadata?: {
214
+ // Only in full mode
218
215
  totalElements: number;
219
216
  extractedElements: number;
220
217
  mainContent?: string;
@@ -233,15 +230,23 @@ interface ExtractedElement {
233
230
  text: string;
234
231
 
235
232
  selector: {
236
- css: string; // Best CSS selector (ranked stable-first)
237
- xpath: string; // XPath selector
238
- textBased?: string; // Text-content based hint
233
+ css: string; // Best CSS selector (ranked stable-first)
234
+ xpath: string; // XPath selector
235
+ textBased?: string; // Text-content based hint
239
236
  dataTestId?: string; // data-testid if available
240
- ariaLabel?: string; // ARIA label if available
237
+ ariaLabel?: string; // ARIA label if available
241
238
  candidates?: Array<{
242
- type: 'id' | 'data-testid' | 'role-aria' | 'name' | 'class-path' | 'css-path' | 'xpath' | 'text';
239
+ type:
240
+ | 'id'
241
+ | 'data-testid'
242
+ | 'role-aria'
243
+ | 'name'
244
+ | 'class-path'
245
+ | 'css-path'
246
+ | 'xpath'
247
+ | 'text';
243
248
  value: string;
244
- score: number; // Higher = more stable/robust
249
+ score: number; // Higher = more stable/robust
245
250
  }>;
246
251
  };
247
252
 
@@ -271,20 +276,21 @@ interface ExtractedElement {
271
276
 
272
277
  ## Options
273
278
 
274
- | Option | Type | Default | Description |
275
- |--------|------|---------|-------------|
276
- | `mode` | `'interactive' \| 'full'` | `'interactive'` | Extraction mode |
277
- | `maxDepth` | `number` | `5` | Maximum traversal depth |
278
- | `includeHidden` | `boolean` | `false` | Include hidden elements |
279
- | `includeShadowDOM` | `boolean` | `true` | Traverse shadow DOM |
280
- | `includeIframes` | `boolean` | `false` | Traverse iframes |
281
- | `viewportOnly` | `boolean` | `false` | Only visible viewport elements |
282
- | `mainContentOnly` | `boolean` | `false` | Focus on main content area |
283
- | `customSelectors` | `string[]` | `[]` | Additional selectors to extract |
279
+ | Option | Type | Default | Description |
280
+ | ------------------ | ------------------------- | --------------- | ------------------------------- |
281
+ | `mode` | `'interactive' \| 'full'` | `'interactive'` | Extraction mode |
282
+ | `maxDepth` | `number` | `5` | Maximum traversal depth |
283
+ | `includeHidden` | `boolean` | `false` | Include hidden elements |
284
+ | `includeShadowDOM` | `boolean` | `true` | Traverse shadow DOM |
285
+ | `includeIframes` | `boolean` | `false` | Traverse iframes |
286
+ | `viewportOnly` | `boolean` | `false` | Only visible viewport elements |
287
+ | `mainContentOnly` | `boolean` | `false` | Focus on main content area |
288
+ | `customSelectors` | `string[]` | `[]` | Additional selectors to extract |
284
289
 
285
290
  ## Use Cases
286
291
 
287
292
  ### AI Userscript Generation (Progressive Approach)
293
+
288
294
  ```typescript
289
295
  // First, understand the page structure
290
296
  const structure = ProgressiveExtractor.extractStructure(document);
@@ -293,31 +299,31 @@ const structure = ProgressiveExtractor.extractStructure(document);
293
299
  const targetRegion = structure.regions.main?.selector || 'body';
294
300
 
295
301
  // Extract detailed information from chosen region
296
- const details = ProgressiveExtractor.extractRegion(
297
- targetRegion,
298
- document,
299
- { mode: 'interactive', viewportOnly: true }
300
- );
302
+ const details = ProgressiveExtractor.extractRegion(targetRegion, document, {
303
+ mode: 'interactive',
304
+ viewportOnly: true,
305
+ });
301
306
 
302
307
  // Generate userscript prompt with focused context
303
308
  const prompt = `
304
309
  Page: ${details.page.title}
305
310
  Main form: ${details.interactive.forms[0]?.selector}
306
- Submit button: ${details.interactive.buttons.find(b => b.text.includes('Submit'))?.selector.css}
311
+ Submit button: ${details.interactive.buttons.find((b) => b.text.includes('Submit'))?.selector.css}
307
312
 
308
313
  Write a userscript to auto-fill and submit this form.
309
314
  `;
310
315
  ```
311
316
 
312
317
  ### Test Automation (Full Extraction)
318
+
313
319
  ```typescript
314
320
  // Get all interactive elements at once
315
321
  const testData = SmartDOMReader.extractInteractive(document, {
316
- customSelectors: ['[data-test]', '[data-cy]']
322
+ customSelectors: ['[data-test]', '[data-cy]'],
317
323
  });
318
324
 
319
325
  // Use multiple selector strategies for robust testing
320
- testData.interactive.buttons.forEach(button => {
326
+ testData.interactive.buttons.forEach((button) => {
321
327
  console.log(`Button: ${button.text}`);
322
328
  console.log(` CSS: ${button.selector.css}`);
323
329
  console.log(` XPath: ${button.selector.xpath}`);
@@ -327,6 +333,7 @@ testData.interactive.buttons.forEach(button => {
327
333
  ```
328
334
 
329
335
  ### Content Analysis (Progressive Approach)
336
+
330
337
  ```typescript
331
338
  // Get structure first
332
339
  const structure = ProgressiveExtractor.extractStructure(document);
@@ -401,6 +408,7 @@ This library is designed to provide:
401
408
  ### How is this different from Cheerio or jsdom?
402
409
 
403
410
  This library is **AI-optimized**:
411
+
404
412
  - Outputs structured data designed for LLM consumption
405
413
  - Provides ranked selectors with stability scores
406
414
  - Progressive extraction minimizes token usage
@@ -421,6 +429,7 @@ const result = await page.evaluate(() => {
421
429
  ### How do selector rankings work?
422
430
 
423
431
  Selectors are ranked by stability (higher = more reliable):
432
+
424
433
  1. **ID selectors** (score: 100) - `#unique-id`
425
434
  2. **data-testid** (score: 90) - `[data-testid="submit"]`
426
435
  3. **ARIA** (score: 80) - `[role="button"][aria-label="Submit"]`
@@ -437,18 +446,19 @@ Progressive extraction can reduce token usage by 80-95% compared to raw HTML, de
437
446
 
438
447
  ## Comparison with Alternatives
439
448
 
440
- | Feature | @mcp-b/smart-dom-reader | Cheerio | jsdom | Raw DOM |
441
- |---------|-------------------------|---------|-------|---------|
442
- | AI-Optimized Output | Yes | No | No | No |
443
- | Ranked Selectors | Yes | No | No | No |
444
- | Token Efficiency | Progressive | N/A | N/A | N/A |
445
- | Shadow DOM | Yes | No | Limited | Yes |
446
- | Browser Environment | Native | Parse only | Simulated | Native |
447
- | Zero Dependencies | Yes | No | No | Yes |
449
+ | Feature | @mcp-b/smart-dom-reader | Cheerio | jsdom | Raw DOM |
450
+ | ------------------- | ----------------------- | ---------- | --------- | ------- |
451
+ | AI-Optimized Output | Yes | No | No | No |
452
+ | Ranked Selectors | Yes | No | No | No |
453
+ | Token Efficiency | Progressive | N/A | N/A | N/A |
454
+ | Shadow DOM | Yes | No | Limited | Yes |
455
+ | Browser Environment | Native | Parse only | Simulated | Native |
456
+ | Zero Dependencies | Yes | No | No | Yes |
448
457
 
449
458
  ## Credits
450
459
 
451
460
  Inspired by:
461
+
452
462
  - [stacking-contexts-inspector](https://github.com/andreadev-it/stacking-contexts-inspector) - DOM traversal techniques
453
463
  - [dom-to-semantic-markdown](https://github.com/romansky/dom-to-semantic-markdown) - Content scoring algorithms
454
464
  - [z-context](https://github.com/gwwar/z-context) - Selector generation approaches
@@ -485,10 +495,10 @@ For AI agents, use the bundled MCP server which returns XML-wrapped Markdown ins
485
495
  - Region: `<page ...>\n <section><![CDATA[ ...markdown... ]]></section>\n</page>`
486
496
  - Content: `<page ...>\n <content><![CDATA[ ...markdown... ]]></content>\n</page>`
487
497
  - Golden path sequence:
488
- 1) `dom_extract_structure` → get page outline and pick a target
489
- 2) `dom_extract_region` → get actionable selectors for that area
490
- 3) Write a script; if unstable, re-run with higher detail or limits
491
- 4) Optional: `dom_extract_content` for readable text context
498
+ 1. `dom_extract_structure` → get page outline and pick a target
499
+ 2. `dom_extract_region` → get actionable selectors for that area
500
+ 3. Write a script; if unstable, re-run with higher detail or limits
501
+ 4. Optional: `dom_extract_content` for readable text context
492
502
 
493
503
  ### Running the server
494
504
 
@@ -534,6 +544,7 @@ pnpm --filter @mcp-b/smart-dom-reader test:local
534
544
  ```
535
545
 
536
546
  What it validates:
547
+
537
548
  - Stable selectors (ID, data-testid, role+aria, name/id)
538
549
  - Semantic extraction (headings/images/tables/lists)
539
550
  - Shadow DOM detection
@@ -0,0 +1,13 @@
1
+ //#region src/bundle-string.d.ts
2
+ /**
3
+ * Auto-generated bundle module for smart-dom-reader
4
+ * DO NOT EDIT - Generated by generate-bundle-module.mjs
5
+ *
6
+ * This module exports the bundled smart-dom-reader library as a string
7
+ * that can be injected into web pages for stateless DOM extraction.
8
+ */
9
+ declare const SMART_DOM_READER_BUNDLE = "var SmartDOMReaderBundle = (function(exports) {\n\tObject.defineProperty(exports, Symbol.toStringTag, { value: \"Module\" });\n\t//#region \\0rolldown/runtime.js\n\tvar __defProp = Object.defineProperty;\n\tvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n\tvar __getOwnPropNames = Object.getOwnPropertyNames;\n\tvar __hasOwnProp = Object.prototype.hasOwnProperty;\n\tvar __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);\n\tvar __exportAll = (all, no_symbols) => {\n\t\tlet target = {};\n\t\tfor (var name in all) __defProp(target, name, {\n\t\t\tget: all[name],\n\t\t\tenumerable: true\n\t\t});\n\t\tif (!no_symbols) __defProp(target, Symbol.toStringTag, { value: \"Module\" });\n\t\treturn target;\n\t};\n\tvar __copyProps = (to, from, except, desc) => {\n\t\tif (from && typeof from === \"object\" || typeof from === \"function\") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {\n\t\t\tkey = keys[i];\n\t\t\tif (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {\n\t\t\t\tget: ((k) => from[k]).bind(null, key),\n\t\t\t\tenumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable\n\t\t\t});\n\t\t}\n\t\treturn to;\n\t};\n\tvar __toCommonJS = (mod) => __hasOwnProp.call(mod, \"module.exports\") ? mod[\"module.exports\"] : __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\t//#endregion\n\t//#region src/content-detection.ts\n\tvar ContentDetection;\n\tvar init_content_detection = __esmMin((() => {\n\t\tContentDetection = class ContentDetection {\n\t\t\t/**\n\t\t\t* Find the main content area of a page\n\t\t\t* Inspired by dom-to-semantic-markdown's approach\n\t\t\t*/\n\t\t\tstatic findMainContent(doc) {\n\t\t\t\tconst mainElement = doc.querySelector(\"main, [role=\\\"main\\\"]\");\n\t\t\t\tif (mainElement) return mainElement;\n\t\t\t\tif (!doc.body) return doc.documentElement;\n\t\t\t\treturn ContentDetection.detectMainContent(doc.body);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Detect main content using scoring algorithm\n\t\t\t*/\n\t\t\tstatic detectMainContent(rootElement) {\n\t\t\t\tconst candidates = [];\n\t\t\t\tContentDetection.collectCandidates(rootElement, candidates, 15);\n\t\t\t\tif (candidates.length === 0) return rootElement;\n\t\t\t\tcandidates.sort((a, b) => ContentDetection.calculateContentScore(b) - ContentDetection.calculateContentScore(a));\n\t\t\t\tlet bestCandidate = candidates[0];\n\t\t\t\tfor (let i = 1; i < candidates.length; i++) {\n\t\t\t\t\tconst candidate = candidates[i];\n\t\t\t\t\tif (!candidates.some((other, j) => j !== i && other.contains(candidate)) && ContentDetection.calculateContentScore(candidate) > ContentDetection.calculateContentScore(bestCandidate)) bestCandidate = candidate;\n\t\t\t\t}\n\t\t\t\treturn bestCandidate;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Collect content candidates\n\t\t\t*/\n\t\t\tstatic collectCandidates(element, candidates, minScore) {\n\t\t\t\tif (ContentDetection.calculateContentScore(element) >= minScore) candidates.push(element);\n\t\t\t\tArray.from(element.children).forEach((child) => {\n\t\t\t\t\tContentDetection.collectCandidates(child, candidates, minScore);\n\t\t\t\t});\n\t\t\t}\n\t\t\t/**\n\t\t\t* Calculate content score for an element\n\t\t\t*/\n\t\t\tstatic calculateContentScore(element) {\n\t\t\t\tlet score = 0;\n\t\t\t\tconst semanticClasses = [\n\t\t\t\t\t\"article\",\n\t\t\t\t\t\"content\",\n\t\t\t\t\t\"main-container\",\n\t\t\t\t\t\"main\",\n\t\t\t\t\t\"main-content\",\n\t\t\t\t\t\"post\",\n\t\t\t\t\t\"entry\"\n\t\t\t\t];\n\t\t\t\tconst semanticIds = [\n\t\t\t\t\t\"content\",\n\t\t\t\t\t\"main\",\n\t\t\t\t\t\"article\",\n\t\t\t\t\t\"post\",\n\t\t\t\t\t\"entry\"\n\t\t\t\t];\n\t\t\t\tsemanticClasses.forEach((cls) => {\n\t\t\t\t\tif (element.classList.contains(cls)) score += 10;\n\t\t\t\t});\n\t\t\t\tsemanticIds.forEach((id) => {\n\t\t\t\t\tif (element.id?.toLowerCase().includes(id)) score += 10;\n\t\t\t\t});\n\t\t\t\tconst tag = element.tagName.toLowerCase();\n\t\t\t\tif ([\n\t\t\t\t\t\"article\",\n\t\t\t\t\t\"main\",\n\t\t\t\t\t\"section\"\n\t\t\t\t].includes(tag)) score += 8;\n\t\t\t\tconst paragraphs = element.getElementsByTagName(\"p\").length;\n\t\t\t\tscore += Math.min(paragraphs * 2, 10);\n\t\t\t\tconst headings = element.querySelectorAll(\"h1, h2, h3\").length;\n\t\t\t\tscore += Math.min(headings * 3, 9);\n\t\t\t\tconst textLength = element.textContent?.trim().length || 0;\n\t\t\t\tif (textLength > 300) score += Math.min(Math.floor(textLength / 300) * 2, 10);\n\t\t\t\tconst linkDensity = ContentDetection.calculateLinkDensity(element);\n\t\t\t\tif (linkDensity < .3) score += 5;\n\t\t\t\telse if (linkDensity > .5) score -= 5;\n\t\t\t\tif (element.hasAttribute(\"data-main\") || element.hasAttribute(\"data-content\") || element.hasAttribute(\"itemprop\")) score += 8;\n\t\t\t\tconst role = element.getAttribute(\"role\");\n\t\t\t\tif (role === \"main\" || role === \"article\") score += 10;\n\t\t\t\tif (element.matches(\"aside, nav, header, footer, .sidebar, .navigation, .menu, .ad, .advertisement\")) score -= 10;\n\t\t\t\tif (element.getElementsByTagName(\"form\").length > 2) score -= 5;\n\t\t\t\treturn Math.max(0, score);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Calculate link density in an element\n\t\t\t*/\n\t\t\tstatic calculateLinkDensity(element) {\n\t\t\t\tconst links = element.getElementsByTagName(\"a\");\n\t\t\t\tlet linkTextLength = 0;\n\t\t\t\tfor (const link of Array.from(links)) linkTextLength += link.textContent?.length || 0;\n\t\t\t\tconst totalTextLength = element.textContent?.length || 1;\n\t\t\t\treturn linkTextLength / totalTextLength;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if an element is likely navigation\n\t\t\t*/\n\t\t\tstatic isNavigation(element) {\n\t\t\t\tif (element.tagName.toLowerCase() === \"nav\" || element.getAttribute(\"role\") === \"navigation\") return true;\n\t\t\t\tconst navPatterns = [\n\t\t\t\t\t/nav/i,\n\t\t\t\t\t/menu/i,\n\t\t\t\t\t/sidebar/i,\n\t\t\t\t\t/toolbar/i\n\t\t\t\t];\n\t\t\t\tconst classesAndId = `${element.className} ${element.id}`.toLowerCase();\n\t\t\t\treturn navPatterns.some((pattern) => pattern.test(classesAndId));\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if element is likely supplementary content\n\t\t\t*/\n\t\t\tstatic isSupplementary(element) {\n\t\t\t\tif (element.tagName.toLowerCase() === \"aside\" || element.getAttribute(\"role\") === \"complementary\") return true;\n\t\t\t\tconst supplementaryPatterns = [\n\t\t\t\t\t/sidebar/i,\n\t\t\t\t\t/widget/i,\n\t\t\t\t\t/related/i,\n\t\t\t\t\t/advertisement/i,\n\t\t\t\t\t/social/i\n\t\t\t\t];\n\t\t\t\tconst classesAndId = `${element.className} ${element.id}`.toLowerCase();\n\t\t\t\treturn supplementaryPatterns.some((pattern) => pattern.test(classesAndId));\n\t\t\t}\n\t\t\t/**\n\t\t\t* Detect page landmarks\n\t\t\t*/\n\t\t\tstatic detectLandmarks(doc) {\n\t\t\t\tconst landmarks = {\n\t\t\t\t\tnavigation: [],\n\t\t\t\t\tmain: [],\n\t\t\t\t\tcomplementary: [],\n\t\t\t\t\tcontentinfo: [],\n\t\t\t\t\tbanner: [],\n\t\t\t\t\tsearch: [],\n\t\t\t\t\tform: [],\n\t\t\t\t\tregion: []\n\t\t\t\t};\n\t\t\t\tfor (const [landmark, selector] of Object.entries({\n\t\t\t\t\tnavigation: \"nav, [role=\\\"navigation\\\"]\",\n\t\t\t\t\tmain: \"main, [role=\\\"main\\\"]\",\n\t\t\t\t\tcomplementary: \"aside, [role=\\\"complementary\\\"]\",\n\t\t\t\t\tcontentinfo: \"footer, [role=\\\"contentinfo\\\"]\",\n\t\t\t\t\tbanner: \"header, [role=\\\"banner\\\"]\",\n\t\t\t\t\tsearch: \"[role=\\\"search\\\"]\",\n\t\t\t\t\tform: \"form[aria-label], form[aria-labelledby], [role=\\\"form\\\"]\",\n\t\t\t\t\tregion: \"section[aria-label], section[aria-labelledby], [role=\\\"region\\\"]\"\n\t\t\t\t})) {\n\t\t\t\t\tconst elements = doc.querySelectorAll(selector);\n\t\t\t\t\tlandmarks[landmark] = Array.from(elements);\n\t\t\t\t}\n\t\t\t\treturn landmarks;\n\t\t\t}\n\t\t};\n\t}));\n\t//#endregion\n\t//#region src/selectors.ts\n\tvar SelectorGenerator;\n\tvar init_selectors = __esmMin((() => {\n\t\tSelectorGenerator = class SelectorGenerator {\n\t\t\t/**\n\t\t\t* Generate multiple selector strategies for an element\n\t\t\t*/\n\t\t\tstatic generateSelectors(element) {\n\t\t\t\tconst doc = element.ownerDocument || document;\n\t\t\t\tconst candidates = [];\n\t\t\t\tif (element.id && SelectorGenerator.isUniqueId(element.id, doc)) candidates.push({\n\t\t\t\t\ttype: \"id\",\n\t\t\t\t\tvalue: `#${CSS.escape(element.id)}`,\n\t\t\t\t\tscore: 100\n\t\t\t\t});\n\t\t\t\tconst testId = SelectorGenerator.getDataTestId(element);\n\t\t\t\tif (testId) {\n\t\t\t\t\tconst v = `[data-testid=\"${CSS.escape(testId)}\"]`;\n\t\t\t\t\tcandidates.push({\n\t\t\t\t\t\ttype: \"data-testid\",\n\t\t\t\t\t\tvalue: v,\n\t\t\t\t\t\tscore: 90 + (SelectorGenerator.isUniqueSelectorSafe(v, doc) ? 5 : 0)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst role = element.getAttribute(\"role\");\n\t\t\t\tconst aria = element.getAttribute(\"aria-label\");\n\t\t\t\tif (role && aria) {\n\t\t\t\t\tconst v = `[role=\"${CSS.escape(role)}\"][aria-label=\"${CSS.escape(aria)}\"]`;\n\t\t\t\t\tcandidates.push({\n\t\t\t\t\t\ttype: \"role-aria\",\n\t\t\t\t\t\tvalue: v,\n\t\t\t\t\t\tscore: 85 + (SelectorGenerator.isUniqueSelectorSafe(v, doc) ? 5 : 0)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst nameAttr = element.getAttribute(\"name\");\n\t\t\t\tif (nameAttr) {\n\t\t\t\t\tconst v = `[name=\"${CSS.escape(nameAttr)}\"]`;\n\t\t\t\t\tcandidates.push({\n\t\t\t\t\t\ttype: \"name\",\n\t\t\t\t\t\tvalue: v,\n\t\t\t\t\t\tscore: 78 + (SelectorGenerator.isUniqueSelectorSafe(v, doc) ? 5 : 0)\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst pathCss = SelectorGenerator.generateCSSSelector(element, doc);\n\t\t\t\tconst structuralPenalty = (pathCss.match(/:nth-child\\(/g) || []).length * 10;\n\t\t\t\tconst classBonus = pathCss.includes(\".\") ? 8 : 0;\n\t\t\t\tconst pathScore = Math.max(0, 70 + classBonus - structuralPenalty);\n\t\t\t\tcandidates.push({\n\t\t\t\t\ttype: \"class-path\",\n\t\t\t\t\tvalue: pathCss,\n\t\t\t\t\tscore: pathScore\n\t\t\t\t});\n\t\t\t\tconst xpath = SelectorGenerator.generateXPath(element, doc);\n\t\t\t\tcandidates.push({\n\t\t\t\t\ttype: \"xpath\",\n\t\t\t\t\tvalue: xpath,\n\t\t\t\t\tscore: 40\n\t\t\t\t});\n\t\t\t\tconst textBased = SelectorGenerator.generateTextBasedSelector(element);\n\t\t\t\tif (textBased) candidates.push({\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tvalue: textBased,\n\t\t\t\t\tscore: 30\n\t\t\t\t});\n\t\t\t\tcandidates.sort((a, b) => b.score - a.score);\n\t\t\t\tconst selector = {\n\t\t\t\t\tcss: candidates.find((c) => c.type !== \"xpath\" && c.type !== \"text\")?.value || pathCss,\n\t\t\t\t\txpath,\n\t\t\t\t\tcandidates\n\t\t\t\t};\n\t\t\t\tif (textBased) selector.textBased = textBased;\n\t\t\t\tif (testId) selector.dataTestId = testId;\n\t\t\t\tif (aria) selector.ariaLabel = aria;\n\t\t\t\treturn selector;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Generate a unique CSS selector for an element\n\t\t\t*/\n\t\t\tstatic generateCSSSelector(element, doc) {\n\t\t\t\tif (element.id && SelectorGenerator.isUniqueId(element.id, doc)) return `#${CSS.escape(element.id)}`;\n\t\t\t\tconst testId = SelectorGenerator.getDataTestId(element);\n\t\t\t\tif (testId) return `[data-testid=\"${CSS.escape(testId)}\"]`;\n\t\t\t\tconst path = [];\n\t\t\t\tlet current = element;\n\t\t\t\twhile (current && current.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\t\tlet selector = current.nodeName.toLowerCase();\n\t\t\t\t\tif (current.id && SelectorGenerator.isUniqueId(current.id, doc)) {\n\t\t\t\t\t\tselector = `#${CSS.escape(current.id)}`;\n\t\t\t\t\t\tpath.unshift(selector);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst classes = SelectorGenerator.getMeaningfulClasses(current);\n\t\t\t\t\tif (classes.length > 0) selector += `.${classes.map((c) => CSS.escape(c)).join(\".\")}`;\n\t\t\t\t\tconst siblings = current.parentElement?.children;\n\t\t\t\t\tif (siblings && siblings.length > 1) {\n\t\t\t\t\t\tconst index = Array.from(siblings).indexOf(current);\n\t\t\t\t\t\tif (index > 0 || !SelectorGenerator.isUniqueSelector(selector, current.parentElement)) selector += `:nth-child(${index + 1})`;\n\t\t\t\t\t}\n\t\t\t\t\tpath.unshift(selector);\n\t\t\t\t\tcurrent = current.parentElement;\n\t\t\t\t}\n\t\t\t\treturn SelectorGenerator.optimizePath(path, element, doc);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Generate XPath for an element\n\t\t\t*/\n\t\t\tstatic generateXPath(element, doc) {\n\t\t\t\tif (element.id && SelectorGenerator.isUniqueId(element.id, doc)) return `//*[@id=\"${element.id}\"]`;\n\t\t\t\tconst path = [];\n\t\t\t\tlet current = element;\n\t\t\t\twhile (current && current.nodeType === Node.ELEMENT_NODE) {\n\t\t\t\t\tconst tagName = current.nodeName.toLowerCase();\n\t\t\t\t\tif (current.id && SelectorGenerator.isUniqueId(current.id, doc)) {\n\t\t\t\t\t\tpath.unshift(`//*[@id=\"${current.id}\"]`);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tlet xpath = tagName;\n\t\t\t\t\tconst siblings = current.parentElement?.children;\n\t\t\t\t\tif (siblings) {\n\t\t\t\t\t\tconst sameTagSiblings = Array.from(siblings).filter((s) => s.nodeName.toLowerCase() === tagName);\n\t\t\t\t\t\tif (sameTagSiblings.length > 1) {\n\t\t\t\t\t\t\tconst index = sameTagSiblings.indexOf(current) + 1;\n\t\t\t\t\t\t\txpath += `[${index}]`;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpath.unshift(xpath);\n\t\t\t\t\tcurrent = current.parentElement;\n\t\t\t\t}\n\t\t\t\treturn `//${path.join(\"/\")}`;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Generate a text-based selector for buttons and links\n\t\t\t*/\n\t\t\tstatic generateTextBasedSelector(element) {\n\t\t\t\tconst text = element.textContent?.trim();\n\t\t\t\tif (!text || text.length > 50) return void 0;\n\t\t\t\tconst tag = element.nodeName.toLowerCase();\n\t\t\t\tif ([\n\t\t\t\t\t\"button\",\n\t\t\t\t\t\"a\",\n\t\t\t\t\t\"label\"\n\t\t\t\t].includes(tag)) return `${tag}:contains(\"${text.replace(/['\"\\\\]/g, \"\\\\$&\")}\")`;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get data-testid or similar attributes\n\t\t\t*/\n\t\t\tstatic getDataTestId(element) {\n\t\t\t\treturn element.getAttribute(\"data-testid\") || element.getAttribute(\"data-test-id\") || element.getAttribute(\"data-test\") || element.getAttribute(\"data-cy\") || void 0;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if an ID is unique in the document\n\t\t\t*/\n\t\t\tstatic isUniqueId(id, doc) {\n\t\t\t\treturn doc.querySelectorAll(`#${CSS.escape(id)}`).length === 1;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if a selector is unique within a container\n\t\t\t*/\n\t\t\tstatic isUniqueSelector(selector, container) {\n\t\t\t\ttry {\n\t\t\t\t\treturn container.querySelectorAll(selector).length === 1;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tstatic isUniqueSelectorSafe(selector, doc) {\n\t\t\t\ttry {\n\t\t\t\t\treturn doc.querySelectorAll(selector).length === 1;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get meaningful classes (filtering out utility classes)\n\t\t\t*/\n\t\t\tstatic getMeaningfulClasses(element) {\n\t\t\t\tconst classes = Array.from(element.classList);\n\t\t\t\tconst utilityPatterns = [\n\t\t\t\t\t/^(p|m|w|h|text|bg|border|flex|grid|col|row)-/,\n\t\t\t\t\t/^(xs|sm|md|lg|xl|2xl):/,\n\t\t\t\t\t/^(hover|focus|active|disabled|checked):/,\n\t\t\t\t\t/^js-/,\n\t\t\t\t\t/^is-/,\n\t\t\t\t\t/^has-/\n\t\t\t\t];\n\t\t\t\treturn classes.filter((cls) => {\n\t\t\t\t\tif (cls.length < 3) return false;\n\t\t\t\t\treturn !utilityPatterns.some((pattern) => pattern.test(cls));\n\t\t\t\t}).slice(0, 2);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Optimize the selector path by removing unnecessary parts\n\t\t\t*/\n\t\t\tstatic optimizePath(path, element, doc) {\n\t\t\t\tfor (let i = 0; i < path.length - 1; i++) {\n\t\t\t\t\tconst shortPath = path.slice(i).join(\" > \");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst matches = doc.querySelectorAll(shortPath);\n\t\t\t\t\t\tif (matches.length === 1 && matches[0] === element) return shortPath;\n\t\t\t\t\t} catch {}\n\t\t\t\t}\n\t\t\t\treturn path.join(\" > \");\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get a human-readable path description\n\t\t\t*/\n\t\t\tstatic getContextPath(element) {\n\t\t\t\tconst path = [];\n\t\t\t\tlet current = element;\n\t\t\t\tlet depth = 0;\n\t\t\t\tconst maxDepth = 5;\n\t\t\t\twhile (current && current !== element.ownerDocument?.body && depth < maxDepth) {\n\t\t\t\t\tconst tag = current.nodeName.toLowerCase();\n\t\t\t\t\tlet descriptor = tag;\n\t\t\t\t\tif (current.id) descriptor = `${tag}#${current.id}`;\n\t\t\t\t\telse if (current.className && typeof current.className === \"string\") {\n\t\t\t\t\t\tconst firstClass = current.className.split(\" \")[0];\n\t\t\t\t\t\tif (firstClass) descriptor = `${tag}.${firstClass}`;\n\t\t\t\t\t}\n\t\t\t\t\tconst role = current.getAttribute(\"role\");\n\t\t\t\t\tif (role) descriptor += `[role=\"${role}\"]`;\n\t\t\t\t\tpath.unshift(descriptor);\n\t\t\t\t\tcurrent = current.parentElement;\n\t\t\t\t\tdepth++;\n\t\t\t\t}\n\t\t\t\treturn path;\n\t\t\t}\n\t\t};\n\t}));\n\t//#endregion\n\t//#region src/traversal.ts\n\tvar DOMTraversal;\n\tvar init_traversal = __esmMin((() => {\n\t\tinit_selectors();\n\t\tDOMTraversal = class DOMTraversal {\n\t\t\tstatic INTERACTIVE_SELECTORS = [\n\t\t\t\t\"button\",\n\t\t\t\t\"a[href]\",\n\t\t\t\t\"input:not([type=\\\"hidden\\\"])\",\n\t\t\t\t\"textarea\",\n\t\t\t\t\"select\",\n\t\t\t\t\"[role=\\\"button\\\"]\",\n\t\t\t\t\"[onclick]\",\n\t\t\t\t\"[contenteditable=\\\"true\\\"]\",\n\t\t\t\t\"summary\",\n\t\t\t\t\"[tabindex]:not([tabindex=\\\"-1\\\"])\"\n\t\t\t];\n\t\t\tstatic SEMANTIC_SELECTORS = [\n\t\t\t\t\"h1\",\n\t\t\t\t\"h2\",\n\t\t\t\t\"h3\",\n\t\t\t\t\"h4\",\n\t\t\t\t\"h5\",\n\t\t\t\t\"h6\",\n\t\t\t\t\"article\",\n\t\t\t\t\"section\",\n\t\t\t\t\"nav\",\n\t\t\t\t\"aside\",\n\t\t\t\t\"main\",\n\t\t\t\t\"header\",\n\t\t\t\t\"footer\",\n\t\t\t\t\"form\",\n\t\t\t\t\"table\",\n\t\t\t\t\"ul\",\n\t\t\t\t\"ol\",\n\t\t\t\t\"img[alt]\",\n\t\t\t\t\"figure\",\n\t\t\t\t\"video\",\n\t\t\t\t\"audio\",\n\t\t\t\t\"[role=\\\"navigation\\\"]\",\n\t\t\t\t\"[role=\\\"main\\\"]\",\n\t\t\t\t\"[role=\\\"complementary\\\"]\",\n\t\t\t\t\"[role=\\\"contentinfo\\\"]\"\n\t\t\t];\n\t\t\t/**\n\t\t\t* Check if element is visible\n\t\t\t*/\n\t\t\tstatic isVisible(element, computedStyle) {\n\t\t\t\tconst rect = element.getBoundingClientRect();\n\t\t\t\tconst style = computedStyle || element.ownerDocument?.defaultView?.getComputedStyle(element);\n\t\t\t\tif (!style) return false;\n\t\t\t\treturn !!(rect.width > 0 && rect.height > 0 && style.display !== \"none\" && style.visibility !== \"hidden\" && style.opacity !== \"0\" && element.offsetParent !== null);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if element is in viewport\n\t\t\t*/\n\t\t\tstatic isInViewport(element, viewport) {\n\t\t\t\tconst rect = element.getBoundingClientRect();\n\t\t\t\tconst view = viewport || {\n\t\t\t\t\twidth: element.ownerDocument?.defaultView?.innerWidth || 0,\n\t\t\t\t\theight: element.ownerDocument?.defaultView?.innerHeight || 0\n\t\t\t\t};\n\t\t\t\treturn rect.top < view.height && rect.bottom > 0 && rect.left < view.width && rect.right > 0;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if element passes filter criteria\n\t\t\t*/\n\t\t\tstatic passesFilter(element, filter) {\n\t\t\t\tif (!filter) return true;\n\t\t\t\tconst htmlElement = element;\n\t\t\t\tif (filter.excludeSelectors?.length) {\n\t\t\t\t\tfor (const selector of filter.excludeSelectors) if (element.matches(selector)) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.includeSelectors?.length) {\n\t\t\t\t\tlet matches = false;\n\t\t\t\t\tfor (const selector of filter.includeSelectors) if (element.matches(selector)) {\n\t\t\t\t\t\tmatches = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!matches) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.tags?.length && !filter.tags.includes(element.tagName.toLowerCase())) return false;\n\t\t\t\tconst textContent = htmlElement.textContent?.toLowerCase() || \"\";\n\t\t\t\tif (filter.textContains?.length) {\n\t\t\t\t\tlet hasText = false;\n\t\t\t\t\tfor (const text of filter.textContains) if (textContent.includes(text.toLowerCase())) {\n\t\t\t\t\t\thasText = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!hasText) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.textMatches?.length) {\n\t\t\t\t\tlet matches = false;\n\t\t\t\t\tfor (const pattern of filter.textMatches) if (pattern.test(textContent)) {\n\t\t\t\t\t\tmatches = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!matches) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.hasAttributes?.length) {\n\t\t\t\t\tfor (const attr of filter.hasAttributes) if (!element.hasAttribute(attr)) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.attributeValues) for (const [attr, value] of Object.entries(filter.attributeValues)) {\n\t\t\t\t\tconst attrValue = element.getAttribute(attr);\n\t\t\t\t\tif (!attrValue) return false;\n\t\t\t\t\tif (typeof value === \"string\") {\n\t\t\t\t\t\tif (attrValue !== value) return false;\n\t\t\t\t\t} else if (value instanceof RegExp) {\n\t\t\t\t\t\tif (!value.test(attrValue)) return false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (filter.withinSelectors?.length) {\n\t\t\t\t\tlet isWithin = false;\n\t\t\t\t\tfor (const selector of filter.withinSelectors) if (element.closest(selector)) {\n\t\t\t\t\t\tisWithin = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!isWithin) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.interactionTypes?.length) {\n\t\t\t\t\tconst interaction = DOMTraversal.getInteractionInfo(element);\n\t\t\t\t\tlet hasInteraction = false;\n\t\t\t\t\tfor (const type of filter.interactionTypes) if (interaction[type]) {\n\t\t\t\t\t\thasInteraction = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!hasInteraction) return false;\n\t\t\t\t}\n\t\t\t\tif (filter.nearText) {\n\t\t\t\t\tconst parent = element.parentElement;\n\t\t\t\t\tif (!parent || !parent.textContent?.toLowerCase().includes(filter.nearText.toLowerCase())) return false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract element information\n\t\t\t*/\n\t\t\tstatic extractElement(element, options, depth = 0) {\n\t\t\t\tif (options.maxDepth && depth > options.maxDepth) return null;\n\t\t\t\tif (!options.includeHidden && !DOMTraversal.isVisible(element)) return null;\n\t\t\t\tif (options.viewportOnly && !DOMTraversal.isInViewport(element)) return null;\n\t\t\t\tif (!DOMTraversal.passesFilter(element, options.filter)) return null;\n\t\t\t\tconst htmlElement = element;\n\t\t\t\tconst extracted = {\n\t\t\t\t\ttag: element.tagName.toLowerCase(),\n\t\t\t\t\ttext: DOMTraversal.getElementText(element, options),\n\t\t\t\t\tselector: SelectorGenerator.generateSelectors(element),\n\t\t\t\t\tattributes: DOMTraversal.getRelevantAttributes(element, options),\n\t\t\t\t\tcontext: DOMTraversal.getElementContext(element),\n\t\t\t\t\tinteraction: DOMTraversal.getInteractionInfo(element)\n\t\t\t\t};\n\t\t\t\tif (options.mode === \"full\" && DOMTraversal.isSemanticContainer(element)) {\n\t\t\t\t\tconst children = [];\n\t\t\t\t\tif (options.includeShadowDOM && htmlElement.shadowRoot) {\n\t\t\t\t\t\tconst shadowChildren = DOMTraversal.extractChildren(htmlElement.shadowRoot, options, depth + 1);\n\t\t\t\t\t\tchildren.push(...shadowChildren);\n\t\t\t\t\t}\n\t\t\t\t\tconst regularChildren = DOMTraversal.extractChildren(element, options, depth + 1);\n\t\t\t\t\tchildren.push(...regularChildren);\n\t\t\t\t\tif (children.length > 0) extracted.children = children;\n\t\t\t\t}\n\t\t\t\treturn extracted;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract children elements\n\t\t\t*/\n\t\t\tstatic extractChildren(container, options, depth) {\n\t\t\t\tconst children = [];\n\t\t\t\tconst elements = container.querySelectorAll(\"*\");\n\t\t\t\tfor (const child of Array.from(elements)) {\n\t\t\t\t\tif (DOMTraversal.hasExtractedAncestor(child, elements)) continue;\n\t\t\t\t\tconst extracted = DOMTraversal.extractElement(child, options, depth);\n\t\t\t\t\tif (extracted) children.push(extracted);\n\t\t\t\t}\n\t\t\t\treturn children;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if element has an ancestor that was already extracted\n\t\t\t*/\n\t\t\tstatic hasExtractedAncestor(element, extractedElements) {\n\t\t\t\tlet parent = element.parentElement;\n\t\t\t\twhile (parent) {\n\t\t\t\t\tif (Array.from(extractedElements).includes(parent)) return true;\n\t\t\t\t\tparent = parent.parentElement;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get relevant attributes for an element\n\t\t\t*/\n\t\t\tstatic getRelevantAttributes(element, options) {\n\t\t\t\tconst relevant = [\n\t\t\t\t\t\"id\",\n\t\t\t\t\t\"class\",\n\t\t\t\t\t\"name\",\n\t\t\t\t\t\"type\",\n\t\t\t\t\t\"value\",\n\t\t\t\t\t\"placeholder\",\n\t\t\t\t\t\"href\",\n\t\t\t\t\t\"src\",\n\t\t\t\t\t\"alt\",\n\t\t\t\t\t\"title\",\n\t\t\t\t\t\"action\",\n\t\t\t\t\t\"method\",\n\t\t\t\t\t\"aria-label\",\n\t\t\t\t\t\"aria-describedby\",\n\t\t\t\t\t\"aria-controls\",\n\t\t\t\t\t\"role\",\n\t\t\t\t\t\"disabled\",\n\t\t\t\t\t\"readonly\",\n\t\t\t\t\t\"required\",\n\t\t\t\t\t\"checked\",\n\t\t\t\t\t\"min\",\n\t\t\t\t\t\"max\",\n\t\t\t\t\t\"pattern\",\n\t\t\t\t\t\"step\",\n\t\t\t\t\t\"autocomplete\",\n\t\t\t\t\t\"data-testid\",\n\t\t\t\t\t\"data-test\",\n\t\t\t\t\t\"data-cy\"\n\t\t\t\t];\n\t\t\t\tconst attributes = {};\n\t\t\t\tconst attrTruncate = options.attributeTruncateLength ?? 100;\n\t\t\t\tconst dataAttrTruncate = options.dataAttributeTruncateLength ?? 50;\n\t\t\t\tfor (const attr of relevant) {\n\t\t\t\t\tconst value = element.getAttribute(attr);\n\t\t\t\t\tif (value) attributes[attr] = value.length > attrTruncate ? `${value.substring(0, attrTruncate)}...` : value;\n\t\t\t\t}\n\t\t\t\tfor (const attr of element.attributes) if (attr.name.startsWith(\"data-\") && !relevant.includes(attr.name)) attributes[attr.name] = attr.value.length > dataAttrTruncate ? `${attr.value.substring(0, dataAttrTruncate)}...` : attr.value;\n\t\t\t\treturn attributes;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get element context information\n\t\t\t*/\n\t\t\tstatic getElementContext(element) {\n\t\t\t\tconst context = { parentChain: SelectorGenerator.getContextPath(element) };\n\t\t\t\tconst form = element.closest(\"form\");\n\t\t\t\tif (form) context.nearestForm = SelectorGenerator.generateSelectors(form).css;\n\t\t\t\tconst section = element.closest(\"section, [role=\\\"region\\\"]\");\n\t\t\t\tif (section) context.nearestSection = SelectorGenerator.generateSelectors(section).css;\n\t\t\t\tconst main = element.closest(\"main, [role=\\\"main\\\"]\");\n\t\t\t\tif (main) context.nearestMain = SelectorGenerator.generateSelectors(main).css;\n\t\t\t\tconst nav = element.closest(\"nav, [role=\\\"navigation\\\"]\");\n\t\t\t\tif (nav) context.nearestNav = SelectorGenerator.generateSelectors(nav).css;\n\t\t\t\treturn context;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get interaction information for an element (compact format)\n\t\t\t*/\n\t\t\tstatic getInteractionInfo(element) {\n\t\t\t\tconst htmlElement = element;\n\t\t\t\tconst interaction = {};\n\t\t\t\tif (!!(htmlElement.onclick || element.getAttribute(\"onclick\") || element.matches(\"button, a[href], [role=\\\"button\\\"], [tabindex]:not([tabindex=\\\"-1\\\"])\"))) interaction.click = true;\n\t\t\t\tif (!!(htmlElement.onchange || element.getAttribute(\"onchange\") || element.matches(\"input, select, textarea\"))) interaction.change = true;\n\t\t\t\tif (!!(htmlElement.onsubmit || element.getAttribute(\"onsubmit\") || element.matches(\"form\"))) interaction.submit = true;\n\t\t\t\tif (element.matches(\"a[href], button[type=\\\"submit\\\"]\")) interaction.nav = true;\n\t\t\t\tif (htmlElement.hasAttribute(\"disabled\") || htmlElement.getAttribute(\"aria-disabled\") === \"true\") interaction.disabled = true;\n\t\t\t\tif (!DOMTraversal.isVisible(element)) interaction.hidden = true;\n\t\t\t\tconst ariaRole = element.getAttribute(\"role\");\n\t\t\t\tif (ariaRole) interaction.role = ariaRole;\n\t\t\t\tif (element.matches(\"input, textarea, select, button\")) {\n\t\t\t\t\tconst form = element.form || element.closest(\"form\");\n\t\t\t\t\tif (form) interaction.form = SelectorGenerator.generateSelectors(form).css;\n\t\t\t\t}\n\t\t\t\treturn interaction;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get text content of an element (limited length)\n\t\t\t*/\n\t\t\tstatic getElementText(element, options) {\n\t\t\t\tif (element.matches(\"input, textarea\")) {\n\t\t\t\t\tconst input = element;\n\t\t\t\t\treturn input.value || input.placeholder || \"\";\n\t\t\t\t}\n\t\t\t\tif (element.matches(\"img\")) return element.alt || \"\";\n\t\t\t\tconst text = element.textContent?.trim() || \"\";\n\t\t\t\tconst maxLength = options?.textTruncateLength;\n\t\t\t\tif (maxLength && text.length > maxLength) return `${text.substring(0, maxLength)}...`;\n\t\t\t\treturn text;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if element is a semantic container\n\t\t\t*/\n\t\t\tstatic isSemanticContainer(element) {\n\t\t\t\treturn element.matches(\"article, section, nav, aside, main, header, footer, form, table, ul, ol, dl, figure, details, dialog, [role=\\\"region\\\"], [role=\\\"navigation\\\"], [role=\\\"main\\\"], [role=\\\"complementary\\\"]\");\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get interactive elements\n\t\t\t*/\n\t\t\tstatic getInteractiveElements(container, options) {\n\t\t\t\tconst elements = [];\n\t\t\t\tconst selector = DOMTraversal.INTERACTIVE_SELECTORS.join(\", \");\n\t\t\t\tconst found = container.querySelectorAll(selector);\n\t\t\t\tfor (const element of Array.from(found)) {\n\t\t\t\t\tconst extracted = DOMTraversal.extractElement(element, options);\n\t\t\t\t\tif (extracted) elements.push(extracted);\n\t\t\t\t}\n\t\t\t\tif (options.customSelectors) for (const customSelector of options.customSelectors) try {\n\t\t\t\t\tconst customFound = container.querySelectorAll(customSelector);\n\t\t\t\t\tfor (const element of Array.from(customFound)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(element, options);\n\t\t\t\t\t\tif (extracted) elements.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t} catch (_e) {\n\t\t\t\t\tconsole.warn(`Invalid custom selector: ${customSelector}`);\n\t\t\t\t}\n\t\t\t\treturn elements;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get semantic elements (for full mode)\n\t\t\t*/\n\t\t\tstatic getSemanticElements(container, options) {\n\t\t\t\tconst elements = [];\n\t\t\t\tconst selector = DOMTraversal.SEMANTIC_SELECTORS.join(\", \");\n\t\t\t\tconst found = container.querySelectorAll(selector);\n\t\t\t\tfor (const element of Array.from(found)) {\n\t\t\t\t\tconst extracted = DOMTraversal.extractElement(element, options);\n\t\t\t\t\tif (extracted) elements.push(extracted);\n\t\t\t\t}\n\t\t\t\treturn elements;\n\t\t\t}\n\t\t};\n\t}));\n\t//#endregion\n\t//#region src/markdown-formatter.ts\n\tfunction truncate(text, len) {\n\t\tconst t = (text ?? \"\").trim();\n\t\tif (!len || t.length <= len) return t;\n\t\tconst keywords = [\n\t\t\t\"login\",\n\t\t\t\"log in\",\n\t\t\t\"sign in\",\n\t\t\t\"sign up\",\n\t\t\t\"submit\",\n\t\t\t\"search\",\n\t\t\t\"filter\",\n\t\t\t\"add to cart\",\n\t\t\t\"next\",\n\t\t\t\"continue\"\n\t\t];\n\t\tconst lower = t.toLowerCase();\n\t\tconst hit = keywords.map((k) => ({\n\t\t\tk,\n\t\t\ti: lower.indexOf(k)\n\t\t})).find((x) => x.i > -1);\n\t\tconst head = Math.max(0, Math.floor(len * .66));\n\t\tif (hit && hit.i > head) {\n\t\t\tconst tailWindow = Math.max(12, len - head - 5);\n\t\t\tconst start = Math.max(0, hit.i - Math.floor(tailWindow / 2));\n\t\t\tconst end = Math.min(t.length, start + tailWindow);\n\t\t\treturn `${t.slice(0, head).trimEnd()} \u2026 ${t.slice(start, end).trim()}\u2026`;\n\t\t}\n\t\tconst slice = t.slice(0, len);\n\t\tconst lastSpace = slice.lastIndexOf(\" \");\n\t\treturn `${lastSpace > 32 ? slice.slice(0, lastSpace) : slice}\u2026`;\n\t}\n\tfunction bestSelector(el) {\n\t\treturn el.selector?.css || \"\";\n\t}\n\tfunction hashId(input) {\n\t\tlet h = 5381;\n\t\tfor (let i = 0; i < input.length; i++) h = h * 33 ^ input.charCodeAt(i);\n\t\treturn `sec-${(h >>> 0).toString(36)}`;\n\t}\n\tfunction iconForRegion(key) {\n\t\tswitch (key) {\n\t\t\tcase \"header\": return \"\uD83E\uDDED\";\n\t\t\tcase \"navigation\": return \"\uD83D\uDCD1\";\n\t\t\tcase \"main\": return \"\uD83D\uDCC4\";\n\t\t\tcase \"sections\": return \"\uD83D\uDDC2\uFE0F\";\n\t\t\tcase \"sidebar\": return \"\uD83D\uDCDA\";\n\t\t\tcase \"footer\": return \"\uD83D\uDD3B\";\n\t\t\tcase \"modals\": return \"\uD83D\uDCAC\";\n\t\t\tdefault: return \"\uD83D\uDD39\";\n\t\t}\n\t}\n\tfunction elementLine(el, opts) {\n\t\tconst txt = truncate(el.text || el.attributes?.ariaLabel, opts?.maxTextLength ?? 80);\n\t\tconst sel = bestSelector(el);\n\t\tconst tag = el.tag.toLowerCase();\n\t\tconst action = el.interaction?.submit ? \"submit\" : el.interaction?.click ? \"click\" : el.interaction?.change ? \"change\" : void 0;\n\t\tconst actionText = action ? ` (${action})` : \"\";\n\t\treturn `- ${tag.toUpperCase()}: ${txt || \"(no text)\"} \u2192 \\`${sel}\\`${actionText}`;\n\t}\n\tfunction selectorQualitySummary(inter) {\n\t\tconst all = [];\n\t\tall.push(...inter.buttons.map((e) => e.selector?.css || \"\"));\n\t\tall.push(...inter.links.map((e) => e.selector?.css || \"\"));\n\t\tall.push(...inter.inputs.map((e) => e.selector?.css || \"\"));\n\t\tall.push(...inter.clickable.map((e) => e.selector?.css || \"\"));\n\t\tconst total = all.length || 1;\n\t\tconst idCount = all.filter((s) => s.startsWith(\"#\")).length;\n\t\tconst testIdCount = all.filter((s) => /\\[data-testid=/.test(s)).length;\n\t\tconst nthCount = all.filter((s) => /:nth-child\\(/.test(s)).length;\n\t\tconst stable = idCount + testIdCount;\n\t\treturn `Selector quality: ${Math.round(stable / total * 100)}% stable (ID/data-testid), ${Math.round(nthCount / total * 100)}% structural (:nth-child)`;\n\t}\n\tfunction renderInteractive(inter, opts) {\n\t\tconst parts = [];\n\t\tconst limit = (arr) => typeof opts?.maxElements === \"number\" ? arr.slice(0, opts.maxElements) : arr;\n\t\tif (inter.buttons.length) {\n\t\t\tparts.push(\"Buttons:\");\n\t\t\tfor (const el of limit(inter.buttons)) parts.push(elementLine(el, opts));\n\t\t}\n\t\tif (inter.links.length) {\n\t\t\tparts.push(\"Links:\");\n\t\t\tfor (const el of limit(inter.links)) parts.push(elementLine(el, opts));\n\t\t}\n\t\tif (inter.inputs.length) {\n\t\t\tparts.push(\"Inputs:\");\n\t\t\tfor (const el of limit(inter.inputs)) parts.push(elementLine(el, opts));\n\t\t}\n\t\tif (inter.clickable.length) {\n\t\t\tparts.push(\"Other Clickable:\");\n\t\t\tfor (const el of limit(inter.clickable)) parts.push(elementLine(el, opts));\n\t\t}\n\t\tif (inter.forms.length) {\n\t\t\tparts.push(\"Forms:\");\n\t\t\tfor (const f of limit(inter.forms)) parts.push(`- FORM: action=${f.action ?? \"-\"} method=${f.method ?? \"-\"} \u2192 \\`${f.selector}\\``);\n\t\t}\n\t\treturn parts.join(\"\\n\");\n\t}\n\tfunction renderRegionInfo(region) {\n\t\tconst icon = iconForRegion(\"region\");\n\t\tconst id = hashId(`${region.selector}|${region.label ?? \"\"}|${region.role ?? \"\"}`);\n\t\tconst label = region.label ? ` ${region.label}` : \"\";\n\t\tconst stats = [];\n\t\tif (region.buttonCount) stats.push(`${region.buttonCount} buttons`);\n\t\tif (region.linkCount) stats.push(`${region.linkCount} links`);\n\t\tif (region.inputCount) stats.push(`${region.inputCount} inputs`);\n\t\tif (region.textPreview) stats.push(`\u201C${truncate(region.textPreview, 80)}\u201D`);\n\t\tconst statsLine = stats.length ? ` \u2014 ${stats.join(\", \")}` : \"\";\n\t\treturn `${icon} ${label} \u2192 \\`${region.selector}\\` [${id}]${statsLine}`;\n\t}\n\tfunction wrapXml(body, meta, type = \"section\") {\n\t\treturn `<page ${[meta?.title ? `title=\"${escapeXml(meta?.title)}\"` : null, meta?.url ? `url=\"${escapeXml(meta?.url)}\"` : null].filter(Boolean).join(\" \")}>\\n <${type}><![CDATA[\\n${body}\\n]]></${type}>\\n</page>`;\n\t}\n\tfunction escapeXml(s) {\n\t\treturn s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\");\n\t}\n\tfunction capitalize(s) {\n\t\treturn s.charAt(0).toUpperCase() + s.slice(1);\n\t}\n\tvar MarkdownFormatter;\n\tvar init_markdown_formatter = __esmMin((() => {\n\t\tMarkdownFormatter = class {\n\t\t\tstatic structure(overview, _opts = {}, meta) {\n\t\t\t\tconst lines = [];\n\t\t\t\tlines.push(\"# Page Outline\");\n\t\t\t\tif (meta?.title || meta?.url) {\n\t\t\t\t\tlines.push(`Title: ${meta?.title ?? \"\"}`.trim());\n\t\t\t\t\tlines.push(`URL: ${meta?.url ?? \"\"}`.trim());\n\t\t\t\t}\n\t\t\t\tlines.push(\"\");\n\t\t\t\tconst regions = overview.regions;\n\t\t\t\tconst entries = [\n\t\t\t\t\t[\"header\", regions.header],\n\t\t\t\t\t[\"navigation\", regions.navigation],\n\t\t\t\t\t[\"main\", regions.main],\n\t\t\t\t\t[\"sections\", regions.sections],\n\t\t\t\t\t[\"sidebar\", regions.sidebar],\n\t\t\t\t\t[\"footer\", regions.footer],\n\t\t\t\t\t[\"modals\", regions.modals]\n\t\t\t\t];\n\t\t\t\tfor (const [key, value] of entries) {\n\t\t\t\t\tif (!value) continue;\n\t\t\t\t\tconst icon = iconForRegion(key);\n\t\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\t\tif (!value.length) continue;\n\t\t\t\t\t\tlines.push(`## ${icon} ${capitalize(key)}`);\n\t\t\t\t\t\tfor (const region of value) lines.push(renderRegionInfo(region));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlines.push(`## ${icon} ${capitalize(key)}`);\n\t\t\t\t\t\tlines.push(renderRegionInfo(value));\n\t\t\t\t\t}\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tif (overview.suggestions?.length) {\n\t\t\t\t\tlines.push(\"## Suggestions\");\n\t\t\t\t\tfor (const s of overview.suggestions) lines.push(`- ${s}`);\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tlines.push(\"Next: choose a region (by selector or [sectionId]) and call dom_extract_region for actionable details.\");\n\t\t\t\treturn wrapXml(lines.join(\"\\n\"), meta, \"outline\");\n\t\t\t}\n\t\t\tstatic region(result, opts = {}, meta) {\n\t\t\t\tconst lines = [];\n\t\t\t\tlines.push(\"# Region Details\");\n\t\t\t\tif (meta?.title || meta?.url) {\n\t\t\t\t\tlines.push(`Title: ${meta?.title ?? \"\"}`.trim());\n\t\t\t\t\tlines.push(`URL: ${meta?.url ?? \"\"}`.trim());\n\t\t\t\t}\n\t\t\t\tlines.push(\"\");\n\t\t\t\tconst inter = result.interactive;\n\t\t\t\tif (result.page) {\n\t\t\t\t\tconst ps = [\n\t\t\t\t\t\tresult.page.hasErrors ? \"errors: yes\" : \"errors: no\",\n\t\t\t\t\t\tresult.page.isLoading ? \"loading: yes\" : \"loading: no\",\n\t\t\t\t\t\tresult.page.hasModals ? \"modals: yes\" : \"modals: no\"\n\t\t\t\t\t];\n\t\t\t\t\tlines.push(`Page state: ${ps.join(\", \")}`);\n\t\t\t\t}\n\t\t\t\tconst summary = [];\n\t\t\t\tconst count = (arr) => arr ? arr.length : 0;\n\t\t\t\tsummary.push(`${count(inter.buttons)} buttons`);\n\t\t\t\tsummary.push(`${count(inter.links)} links`);\n\t\t\t\tsummary.push(`${count(inter.inputs)} inputs`);\n\t\t\t\tif (inter.forms?.length) summary.push(`${count(inter.forms)} forms`);\n\t\t\t\tlines.push(`Summary: ${summary.join(\", \")}`);\n\t\t\t\tlines.push(selectorQualitySummary(inter));\n\t\t\t\tlines.push(\"\");\n\t\t\t\tlines.push(renderInteractive(inter, opts));\n\t\t\t\tlines.push(\"\");\n\t\t\t\tlines.push(\"Next: write a script using the most stable selectors above. If selectors look unstable, rerun dom_extract_region with higher detail or call dom_extract_content for text context.\");\n\t\t\t\treturn wrapXml(lines.join(\"\\n\"), meta, \"section\");\n\t\t\t}\n\t\t\tstatic content(content, opts = {}, meta) {\n\t\t\t\tconst lines = [];\n\t\t\t\tlines.push(\"# Content\");\n\t\t\t\tlines.push(`Selector: \\`${content.selector}\\``);\n\t\t\t\tlines.push(\"\");\n\t\t\t\tif (content.text.headings?.length) {\n\t\t\t\t\tlines.push(\"Headings:\");\n\t\t\t\t\tfor (const h of content.text.headings) lines.push(`- H${h.level}: ${truncate(h.text, opts.maxTextLength ?? 120)}`);\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tif (content.text.paragraphs?.length) {\n\t\t\t\t\tconst limit = typeof opts.maxElements === \"number\" ? opts.maxElements : content.text.paragraphs.length;\n\t\t\t\t\tlines.push(\"Paragraphs:\");\n\t\t\t\t\tfor (const p of content.text.paragraphs.slice(0, limit)) lines.push(`- ${truncate(p, opts.maxTextLength ?? 200)}`);\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tif (content.text.lists?.length) {\n\t\t\t\t\tlines.push(\"Lists:\");\n\t\t\t\t\tfor (const list of content.text.lists) {\n\t\t\t\t\t\tlines.push(`- ${list.type.toUpperCase()}:`);\n\t\t\t\t\t\tconst limit = typeof opts.maxElements === \"number\" ? opts.maxElements : list.items.length;\n\t\t\t\t\t\tfor (const item of list.items.slice(0, limit)) lines.push(` - ${truncate(item, opts.maxTextLength ?? 120)}`);\n\t\t\t\t\t}\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tif (content.tables?.length) {\n\t\t\t\t\tlines.push(\"Tables:\");\n\t\t\t\t\tfor (const t of content.tables) {\n\t\t\t\t\t\tlines.push(`- Headers: ${t.headers.join(\" | \")}`);\n\t\t\t\t\t\tconst limit = typeof opts.maxElements === \"number\" ? opts.maxElements : t.rows.length;\n\t\t\t\t\t\tfor (const row of t.rows.slice(0, limit)) lines.push(` - ${row.join(\" | \")}`);\n\t\t\t\t\t}\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tif (content.media?.length) {\n\t\t\t\t\tlines.push(\"Media:\");\n\t\t\t\t\tconst limit = typeof opts.maxElements === \"number\" ? opts.maxElements : content.media.length;\n\t\t\t\t\tfor (const m of content.media.slice(0, limit)) lines.push(`- ${m.type.toUpperCase()}: ${m.alt ?? \"\"} ${m.src ? `\u2192 ${m.src}` : \"\"}`.trim());\n\t\t\t\t\tlines.push(\"\");\n\t\t\t\t}\n\t\t\t\tlines.push(\"Next: if text is insufficient for targeting, call dom_extract_region for interactive selectors.\");\n\t\t\t\treturn wrapXml(lines.join(\"\\n\"), meta, \"content\");\n\t\t\t}\n\t\t};\n\t}));\n\t//#endregion\n\t//#region src/progressive.ts\n\tfunction resolveSmartDomReader() {\n\t\tif (typeof window !== \"undefined\") {\n\t\t\tconst globalWindow = window;\n\t\t\tconst direct = globalWindow.SmartDOMReader;\n\t\t\tif (typeof direct === \"function\") return direct;\n\t\t\tconst namespace = globalWindow.SmartDOMReaderNamespace;\n\t\t\tif (namespace && typeof namespace.SmartDOMReader === \"function\") return namespace.SmartDOMReader;\n\t\t}\n\t\ttry {\n\t\t\tif (typeof require === \"function\") {\n\t\t\t\tconst moduleExports = (init_src(), __toCommonJS(src_exports));\n\t\t\t\tif (moduleExports && typeof moduleExports.SmartDOMReader === \"function\") return moduleExports.SmartDOMReader;\n\t\t\t\tif (moduleExports && typeof moduleExports.default === \"function\") return moduleExports.default;\n\t\t\t}\n\t\t} catch {}\n\t}\n\tvar ProgressiveExtractor;\n\tvar init_progressive = __esmMin((() => {\n\t\tinit_content_detection();\n\t\tinit_selectors();\n\t\tinit_traversal();\n\t\tProgressiveExtractor = class ProgressiveExtractor {\n\t\t\t/**\n\t\t\t* Step 1: Extract high-level structural overview\n\t\t\t* This provides a \"map\" of the page for the AI to understand structure\n\t\t\t*/\n\t\t\tstatic extractStructure(root) {\n\t\t\t\tconst regions = {};\n\t\t\t\tconst header = root.querySelector(\"header, [role=\\\"banner\\\"], .header, #header\");\n\t\t\t\tif (header) regions.header = ProgressiveExtractor.analyzeRegion(header);\n\t\t\t\tconst navs = root.querySelectorAll(\"nav, [role=\\\"navigation\\\"], .nav, .navigation\");\n\t\t\t\tif (navs.length > 0) regions.navigation = Array.from(navs).map((nav) => ProgressiveExtractor.analyzeRegion(nav));\n\t\t\t\tif (root instanceof Document) {\n\t\t\t\t\tconst main = ContentDetection.findMainContent(root);\n\t\t\t\t\tif (main) {\n\t\t\t\t\t\tregions.main = ProgressiveExtractor.analyzeRegion(main);\n\t\t\t\t\t\tconst sections = main.querySelectorAll(\"section, article, [role=\\\"region\\\"]\");\n\t\t\t\t\t\tif (sections.length > 0) regions.sections = Array.from(sections).filter((section) => !section.closest(\"nav, header, footer\")).map((section) => ProgressiveExtractor.analyzeRegion(section));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tregions.main = ProgressiveExtractor.analyzeRegion(root);\n\t\t\t\t\tconst sections = root.querySelectorAll(\"section, article, [role=\\\"region\\\"]\");\n\t\t\t\t\tif (sections.length > 0) regions.sections = Array.from(sections).filter((section) => !section.closest(\"nav, header, footer\")).map((section) => ProgressiveExtractor.analyzeRegion(section));\n\t\t\t\t}\n\t\t\t\tconst sidebars = root.querySelectorAll(\"aside, [role=\\\"complementary\\\"], .sidebar, #sidebar\");\n\t\t\t\tif (sidebars.length > 0) regions.sidebar = Array.from(sidebars).map((sidebar) => ProgressiveExtractor.analyzeRegion(sidebar));\n\t\t\t\tconst footer = root.querySelector(\"footer, [role=\\\"contentinfo\\\"], .footer, #footer\");\n\t\t\t\tif (footer) regions.footer = ProgressiveExtractor.analyzeRegion(footer);\n\t\t\t\tconst modals = root.querySelectorAll(\"[role=\\\"dialog\\\"], .modal, .popup, .overlay\");\n\t\t\t\tconst visibleModals = Array.from(modals).filter((modal) => DOMTraversal.isVisible(modal));\n\t\t\t\tif (visibleModals.length > 0) regions.modals = visibleModals.map((modal) => ProgressiveExtractor.analyzeRegion(modal));\n\t\t\t\tconst forms = ProgressiveExtractor.extractFormOverview(root);\n\t\t\t\tconst summary = ProgressiveExtractor.calculateSummary(root, regions, forms);\n\t\t\t\treturn {\n\t\t\t\t\tregions,\n\t\t\t\t\tforms,\n\t\t\t\t\tsummary,\n\t\t\t\t\tsuggestions: ProgressiveExtractor.generateSuggestions(regions, summary)\n\t\t\t\t};\n\t\t\t}\n\t\t\t/**\n\t\t\t* Step 2: Extract detailed information from a specific region\n\t\t\t*/\n\t\t\tstatic extractRegion(selector, doc, options = {}, smartDomReaderCtor) {\n\t\t\t\tconst element = doc.querySelector(selector);\n\t\t\t\tif (!element) return null;\n\t\t\t\tconst SmartDOMReaderCtor = smartDomReaderCtor ?? resolveSmartDomReader();\n\t\t\t\tif (!SmartDOMReaderCtor) throw new Error(\"SmartDOMReader is unavailable. Ensure the Smart DOM Reader module is loaded before calling extractRegion.\");\n\t\t\t\treturn new SmartDOMReaderCtor(options).extract(element, options);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Step 3: Extract readable content from a region\n\t\t\t*/\n\t\t\tstatic extractContent(selector, doc, options = {}) {\n\t\t\t\tconst element = doc.querySelector(selector);\n\t\t\t\tif (!element) return null;\n\t\t\t\tconst result = {\n\t\t\t\t\tselector,\n\t\t\t\t\ttext: {},\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\twordCount: 0,\n\t\t\t\t\t\thasInteractive: false\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tif (options.includeHeadings !== false) {\n\t\t\t\t\tconst headings = element.querySelectorAll(\"h1, h2, h3, h4, h5, h6\");\n\t\t\t\t\tresult.text.headings = Array.from(headings).map((h) => ({\n\t\t\t\t\t\tlevel: Number.parseInt(h.tagName[1], 10),\n\t\t\t\t\t\ttext: ProgressiveExtractor.getTextContent(h, options.maxTextLength)\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\tconst paragraphs = element.querySelectorAll(\"p\");\n\t\t\t\tif (paragraphs.length > 0) result.text.paragraphs = Array.from(paragraphs).map((p) => ProgressiveExtractor.getTextContent(p, options.maxTextLength)).filter((text) => text.length > 0);\n\t\t\t\tif (options.includeLists !== false) {\n\t\t\t\t\tconst lists = element.querySelectorAll(\"ul, ol\");\n\t\t\t\t\tresult.text.lists = Array.from(lists).map((list) => ({\n\t\t\t\t\t\ttype: list.tagName.toLowerCase(),\n\t\t\t\t\t\titems: Array.from(list.querySelectorAll(\"li\")).map((li) => ProgressiveExtractor.getTextContent(li, options.maxTextLength))\n\t\t\t\t\t}));\n\t\t\t\t}\n\t\t\t\tif (options.includeTables !== false) {\n\t\t\t\t\tconst tables = element.querySelectorAll(\"table\");\n\t\t\t\t\tresult.tables = Array.from(tables).map((table) => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\theaders: Array.from(table.querySelectorAll(\"th\")).map((th) => ProgressiveExtractor.getTextContent(th)),\n\t\t\t\t\t\t\trows: Array.from(table.querySelectorAll(\"tr\")).filter((tr) => tr.querySelector(\"td\")).map((tr) => Array.from(tr.querySelectorAll(\"td\")).map((td) => ProgressiveExtractor.getTextContent(td)))\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options.includeMedia !== false) {\n\t\t\t\t\tconst images = element.querySelectorAll(\"img\");\n\t\t\t\t\tconst videos = element.querySelectorAll(\"video\");\n\t\t\t\t\tconst audios = element.querySelectorAll(\"audio\");\n\t\t\t\t\tresult.media = [\n\t\t\t\t\t\t...Array.from(images).map((img) => {\n\t\t\t\t\t\t\tconst item = { type: \"img\" };\n\t\t\t\t\t\t\tconst alt = img.getAttribute(\"alt\");\n\t\t\t\t\t\t\tconst src = img.getAttribute(\"src\");\n\t\t\t\t\t\t\tif (alt) item.alt = alt;\n\t\t\t\t\t\t\tif (src) item.src = src;\n\t\t\t\t\t\t\treturn item;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t...Array.from(videos).map((video) => {\n\t\t\t\t\t\t\tconst item = { type: \"video\" };\n\t\t\t\t\t\t\tconst src = video.getAttribute(\"src\");\n\t\t\t\t\t\t\tif (src) item.src = src;\n\t\t\t\t\t\t\treturn item;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t...Array.from(audios).map((audio) => {\n\t\t\t\t\t\t\tconst item = { type: \"audio\" };\n\t\t\t\t\t\t\tconst src = audio.getAttribute(\"src\");\n\t\t\t\t\t\t\tif (src) item.src = src;\n\t\t\t\t\t\t\treturn item;\n\t\t\t\t\t\t})\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\tconst allText = element.textContent || \"\";\n\t\t\t\tresult.metadata.wordCount = allText.trim().split(/\\s+/).length;\n\t\t\t\tresult.metadata.hasInteractive = element.querySelectorAll(\"button, a, input, textarea, select\").length > 0;\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Analyze a region and extract summary information\n\t\t\t*/\n\t\t\tstatic analyzeRegion(element) {\n\t\t\t\tconst selector = SelectorGenerator.generateSelectors(element).css;\n\t\t\t\tconst buttons = element.querySelectorAll(\"button, [role=\\\"button\\\"]\");\n\t\t\t\tconst links = element.querySelectorAll(\"a[href]\");\n\t\t\t\tconst inputs = element.querySelectorAll(\"input, textarea, select\");\n\t\t\t\tconst forms = element.querySelectorAll(\"form\");\n\t\t\t\tconst lists = element.querySelectorAll(\"ul, ol\");\n\t\t\t\tconst tables = element.querySelectorAll(\"table\");\n\t\t\t\tconst media = element.querySelectorAll(\"img, video, audio\");\n\t\t\t\tconst interactiveCount = buttons.length + links.length + inputs.length;\n\t\t\t\tlet label;\n\t\t\t\tconst ariaLabel = element.getAttribute(\"aria-label\");\n\t\t\t\tif (ariaLabel) label = ariaLabel;\n\t\t\t\telse if (element.getAttribute(\"aria-labelledby\")) {\n\t\t\t\t\tconst labelId = element.getAttribute(\"aria-labelledby\");\n\t\t\t\t\tif (labelId) {\n\t\t\t\t\t\tconst labelElement = element.ownerDocument?.getElementById(labelId);\n\t\t\t\t\t\tif (labelElement) label = labelElement.textContent?.trim();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst heading = element.querySelector(\"h1, h2, h3\");\n\t\t\t\t\tif (heading) label = heading.textContent?.trim();\n\t\t\t\t}\n\t\t\t\tconst textContent = element.textContent?.trim() || \"\";\n\t\t\t\tconst textPreview = textContent.length > 50 ? `${textContent.substring(0, 50)}...` : textContent;\n\t\t\t\tconst regionInfo = {\n\t\t\t\t\tselector,\n\t\t\t\t\tinteractiveCount,\n\t\t\t\t\thasForm: forms.length > 0,\n\t\t\t\t\thasList: lists.length > 0,\n\t\t\t\t\thasTable: tables.length > 0,\n\t\t\t\t\thasMedia: media.length > 0\n\t\t\t\t};\n\t\t\t\tif (label) regionInfo.label = label;\n\t\t\t\tconst role = element.getAttribute(\"role\");\n\t\t\t\tif (role) regionInfo.role = role;\n\t\t\t\tif (buttons.length > 0) regionInfo.buttonCount = buttons.length;\n\t\t\t\tif (links.length > 0) regionInfo.linkCount = links.length;\n\t\t\t\tif (inputs.length > 0) regionInfo.inputCount = inputs.length;\n\t\t\t\tif (textPreview.length > 0) regionInfo.textPreview = textPreview;\n\t\t\t\treturn regionInfo;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract overview of forms on the page\n\t\t\t*/\n\t\t\tstatic extractFormOverview(root) {\n\t\t\t\tconst forms = root.querySelectorAll(\"form\");\n\t\t\t\treturn Array.from(forms).map((form) => {\n\t\t\t\t\tconst inputs = form.querySelectorAll(\"input, textarea, select\");\n\t\t\t\t\tconst selector = SelectorGenerator.generateSelectors(form).css;\n\t\t\t\t\tlet location = \"unknown\";\n\t\t\t\t\tif (form.closest(\"header, [role=\\\"banner\\\"]\")) location = \"header\";\n\t\t\t\t\telse if (form.closest(\"nav, [role=\\\"navigation\\\"]\")) location = \"navigation\";\n\t\t\t\t\telse if (form.closest(\"main, [role=\\\"main\\\"]\")) location = \"main\";\n\t\t\t\t\telse if (form.closest(\"aside, [role=\\\"complementary\\\"]\")) location = \"sidebar\";\n\t\t\t\t\telse if (form.closest(\"footer, [role=\\\"contentinfo\\\"]\")) location = \"footer\";\n\t\t\t\t\tlet purpose;\n\t\t\t\t\tconst formId = form.getAttribute(\"id\")?.toLowerCase();\n\t\t\t\t\tconst formClass = form.getAttribute(\"class\")?.toLowerCase();\n\t\t\t\t\tconst formAction = form.getAttribute(\"action\")?.toLowerCase();\n\t\t\t\t\tconst hasEmail = form.querySelector(\"input[type=\\\"email\\\"]\");\n\t\t\t\t\tconst hasPassword = form.querySelector(\"input[type=\\\"password\\\"]\");\n\t\t\t\t\tif (form.querySelector(\"input[type=\\\"search\\\"]\") || formId?.includes(\"search\") || formClass?.includes(\"search\")) purpose = \"search\";\n\t\t\t\t\telse if (hasPassword && hasEmail) purpose = \"login\";\n\t\t\t\t\telse if (hasPassword) purpose = \"authentication\";\n\t\t\t\t\telse if (formId?.includes(\"contact\") || formClass?.includes(\"contact\")) purpose = \"contact\";\n\t\t\t\t\telse if (formId?.includes(\"subscribe\") || formClass?.includes(\"subscribe\")) purpose = \"subscription\";\n\t\t\t\t\telse if (formAction?.includes(\"checkout\") || formClass?.includes(\"checkout\")) purpose = \"checkout\";\n\t\t\t\t\tconst formOverview = {\n\t\t\t\t\t\tselector,\n\t\t\t\t\t\tlocation,\n\t\t\t\t\t\tinputCount: inputs.length\n\t\t\t\t\t};\n\t\t\t\t\tif (purpose) formOverview.purpose = purpose;\n\t\t\t\t\treturn formOverview;\n\t\t\t\t});\n\t\t\t}\n\t\t\t/**\n\t\t\t* Calculate summary statistics\n\t\t\t*/\n\t\t\tstatic calculateSummary(root, regions, forms) {\n\t\t\t\tconst allInteractive = root.querySelectorAll(\"button, a[href], input, textarea, select\");\n\t\t\t\tconst allSections = root.querySelectorAll(\"section, article, [role=\\\"region\\\"]\");\n\t\t\t\tconst hasModals = (regions.modals?.length || 0) > 0;\n\t\t\t\tconst hasErrors = [\n\t\t\t\t\t\".error\",\n\t\t\t\t\t\".alert-danger\",\n\t\t\t\t\t\"[role=\\\"alert\\\"]\"\n\t\t\t\t].some((sel) => {\n\t\t\t\t\tconst element = root.querySelector(sel);\n\t\t\t\t\treturn element ? DOMTraversal.isVisible(element) : false;\n\t\t\t\t});\n\t\t\t\tconst isLoading = [\n\t\t\t\t\t\".loading\",\n\t\t\t\t\t\".spinner\",\n\t\t\t\t\t\"[aria-busy=\\\"true\\\"]\"\n\t\t\t\t].some((sel) => {\n\t\t\t\t\tconst element = root.querySelector(sel);\n\t\t\t\t\treturn element ? DOMTraversal.isVisible(element) : false;\n\t\t\t\t});\n\t\t\t\tconst summary = {\n\t\t\t\t\ttotalInteractive: allInteractive.length,\n\t\t\t\t\ttotalForms: forms.length,\n\t\t\t\t\ttotalSections: allSections.length,\n\t\t\t\t\thasModals,\n\t\t\t\t\thasErrors,\n\t\t\t\t\tisLoading\n\t\t\t\t};\n\t\t\t\tconst mainContentSelector = regions.main?.selector;\n\t\t\t\tif (mainContentSelector) summary.mainContentSelector = mainContentSelector;\n\t\t\t\treturn summary;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Generate AI-friendly suggestions\n\t\t\t*/\n\t\t\tstatic generateSuggestions(regions, summary) {\n\t\t\t\tconst suggestions = [];\n\t\t\t\tif (summary.hasErrors) suggestions.push(\"Page has error indicators - check error messages before interacting\");\n\t\t\t\tif (summary.isLoading) suggestions.push(\"Page appears to be loading - wait or check loading state\");\n\t\t\t\tif (summary.hasModals) suggestions.push(\"Modal/dialog is open - may need to interact with or close it first\");\n\t\t\t\tif (regions.main && regions.main.interactiveCount > 10) suggestions.push(`Main content has ${regions.main.interactiveCount} interactive elements - consider filtering`);\n\t\t\t\tif (summary.totalForms > 0) suggestions.push(`Found ${summary.totalForms} form(s) on the page`);\n\t\t\t\tif (!regions.main) suggestions.push(\"No clear main content area detected - may need to explore regions\");\n\t\t\t\treturn suggestions;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get text content with optional truncation\n\t\t\t*/\n\t\t\tstatic getTextContent(element, maxLength) {\n\t\t\t\tconst text = element.textContent?.trim() || \"\";\n\t\t\t\tif (maxLength && text.length > maxLength) return `${text.substring(0, maxLength)}...`;\n\t\t\t\treturn text;\n\t\t\t}\n\t\t};\n\t}));\n\t//#endregion\n\t//#region src/types.ts\n\tvar init_types = __esmMin((() => {}));\n\t//#endregion\n\t//#region src/index.ts\n\tvar src_exports = /* @__PURE__ */ __exportAll({\n\t\tContentDetection: () => ContentDetection,\n\t\tMarkdownFormatter: () => MarkdownFormatter,\n\t\tProgressiveExtractor: () => ProgressiveExtractor,\n\t\tSelectorGenerator: () => SelectorGenerator,\n\t\tSmartDOMReader: () => SmartDOMReader,\n\t\tdefault: () => SmartDOMReader\n\t});\n\tvar SmartDOMReader;\n\tvar init_src = __esmMin((() => {\n\t\tinit_content_detection();\n\t\tinit_selectors();\n\t\tinit_traversal();\n\t\tinit_markdown_formatter();\n\t\tinit_progressive();\n\t\tinit_types();\n\t\tSmartDOMReader = class SmartDOMReader {\n\t\t\toptions;\n\t\t\tconstructor(options = {}) {\n\t\t\t\tthis.options = {\n\t\t\t\t\tmode: options.mode || \"interactive\",\n\t\t\t\t\tmaxDepth: options.maxDepth || 5,\n\t\t\t\t\tincludeHidden: options.includeHidden || false,\n\t\t\t\t\tincludeShadowDOM: options.includeShadowDOM ?? true,\n\t\t\t\t\tincludeIframes: options.includeIframes || false,\n\t\t\t\t\tviewportOnly: options.viewportOnly || false,\n\t\t\t\t\tmainContentOnly: options.mainContentOnly || false,\n\t\t\t\t\tcustomSelectors: options.customSelectors || [],\n\t\t\t\t\t...options.attributeTruncateLength !== void 0 && { attributeTruncateLength: options.attributeTruncateLength },\n\t\t\t\t\t...options.dataAttributeTruncateLength !== void 0 && { dataAttributeTruncateLength: options.dataAttributeTruncateLength },\n\t\t\t\t\t...options.textTruncateLength !== void 0 && { textTruncateLength: options.textTruncateLength },\n\t\t\t\t\t...options.filter !== void 0 && { filter: options.filter }\n\t\t\t\t};\n\t\t\t}\n\t\t\t/**\n\t\t\t* Main extraction method - extracts all data in one pass\n\t\t\t* @param rootElement The document or element to extract from\n\t\t\t* @param runtimeOptions Options to override constructor options\n\t\t\t*/\n\t\t\textract(rootElement = document, runtimeOptions) {\n\t\t\t\tconst startTime = Date.now();\n\t\t\t\tconst doc = rootElement instanceof Document ? rootElement : rootElement.ownerDocument;\n\t\t\t\tconst options = {\n\t\t\t\t\t...this.options,\n\t\t\t\t\t...runtimeOptions\n\t\t\t\t};\n\t\t\t\tlet container = rootElement instanceof Document ? doc : rootElement;\n\t\t\t\tif (options.mainContentOnly && rootElement instanceof Document) container = ContentDetection.findMainContent(doc);\n\t\t\t\tconst pageState = this.extractPageState(doc);\n\t\t\t\tconst landmarks = this.extractLandmarks(doc);\n\t\t\t\tconst interactive = this.extractInteractiveElements(container, options);\n\t\t\t\tconst result = {\n\t\t\t\t\tmode: options.mode,\n\t\t\t\t\ttimestamp: startTime,\n\t\t\t\t\tpage: pageState,\n\t\t\t\t\tlandmarks,\n\t\t\t\t\tinteractive\n\t\t\t\t};\n\t\t\t\tif (options.mode === \"full\") {\n\t\t\t\t\tconst semantic = this.extractSemanticElements(container, options);\n\t\t\t\t\tconst metadata = this.extractMetadata(doc, container, options);\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...result,\n\t\t\t\t\t\tsemantic,\n\t\t\t\t\t\tmetadata\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract page state information\n\t\t\t*/\n\t\t\textractPageState(doc) {\n\t\t\t\tconst hasFocus = this.getFocusedElement(doc);\n\t\t\t\treturn {\n\t\t\t\t\turl: doc.location?.href || \"\",\n\t\t\t\t\ttitle: doc.title || \"\",\n\t\t\t\t\thasErrors: this.detectErrors(doc),\n\t\t\t\t\tisLoading: this.detectLoading(doc),\n\t\t\t\t\thasModals: this.detectModals(doc),\n\t\t\t\t\t...hasFocus !== void 0 && { hasFocus }\n\t\t\t\t};\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract page landmarks\n\t\t\t*/\n\t\t\textractLandmarks(doc) {\n\t\t\t\tconst detected = ContentDetection.detectLandmarks(doc);\n\t\t\t\treturn {\n\t\t\t\t\tnavigation: this.elementsToSelectors(detected.navigation || []),\n\t\t\t\t\tmain: this.elementsToSelectors(detected.main || []),\n\t\t\t\t\tforms: this.elementsToSelectors(detected.form || []),\n\t\t\t\t\theaders: this.elementsToSelectors(detected.banner || []),\n\t\t\t\t\tfooters: this.elementsToSelectors(detected.contentinfo || []),\n\t\t\t\t\tarticles: this.elementsToSelectors(detected.region || []),\n\t\t\t\t\tsections: this.elementsToSelectors(detected.region || [])\n\t\t\t\t};\n\t\t\t}\n\t\t\t/**\n\t\t\t* Convert elements to selector strings\n\t\t\t*/\n\t\t\telementsToSelectors(elements) {\n\t\t\t\treturn elements.map((el) => SelectorGenerator.generateSelectors(el).css);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract interactive elements\n\t\t\t*/\n\t\t\textractInteractiveElements(container, options) {\n\t\t\t\tconst buttons = [];\n\t\t\t\tconst links = [];\n\t\t\t\tconst inputs = [];\n\t\t\t\tconst clickable = [];\n\t\t\t\tcontainer.querySelectorAll(\"button, [role=\\\"button\\\"], input[type=\\\"button\\\"], input[type=\\\"submit\\\"]\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) buttons.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tcontainer.querySelectorAll(\"a[href]\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) links.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tcontainer.querySelectorAll(\"input:not([type=\\\"button\\\"]):not([type=\\\"submit\\\"]), textarea, select\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) inputs.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (options.customSelectors) options.customSelectors.forEach((selector) => {\n\t\t\t\t\tcontainer.querySelectorAll(selector).forEach((el) => {\n\t\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\t\tif (extracted) clickable.push(extracted);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tbuttons,\n\t\t\t\t\tlinks,\n\t\t\t\t\tinputs,\n\t\t\t\t\tforms: this.extractForms(container, options),\n\t\t\t\t\tclickable\n\t\t\t\t};\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract form information\n\t\t\t*/\n\t\t\textractForms(container, options) {\n\t\t\t\tconst forms = [];\n\t\t\t\tcontainer.querySelectorAll(\"form\").forEach((form) => {\n\t\t\t\t\tif (!this.shouldIncludeElement(form, options)) return;\n\t\t\t\t\tconst formInputs = [];\n\t\t\t\t\tconst formButtons = [];\n\t\t\t\t\tform.querySelectorAll(\"input:not([type=\\\"button\\\"]):not([type=\\\"submit\\\"]), textarea, select\").forEach((input) => {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(input, options);\n\t\t\t\t\t\tif (extracted) formInputs.push(extracted);\n\t\t\t\t\t});\n\t\t\t\t\tform.querySelectorAll(\"button, input[type=\\\"button\\\"], input[type=\\\"submit\\\"]\").forEach((button) => {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(button, options);\n\t\t\t\t\t\tif (extracted) formButtons.push(extracted);\n\t\t\t\t\t});\n\t\t\t\t\tconst action = form.getAttribute(\"action\");\n\t\t\t\t\tconst method = form.getAttribute(\"method\");\n\t\t\t\t\tconst formInfo = {\n\t\t\t\t\t\tselector: SelectorGenerator.generateSelectors(form).css,\n\t\t\t\t\t\tinputs: formInputs,\n\t\t\t\t\t\tbuttons: formButtons\n\t\t\t\t\t};\n\t\t\t\t\tif (action) formInfo.action = action;\n\t\t\t\t\tif (method) formInfo.method = method;\n\t\t\t\t\tforms.push(formInfo);\n\t\t\t\t});\n\t\t\t\treturn forms;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract semantic elements (full mode only)\n\t\t\t*/\n\t\t\textractSemanticElements(container, options) {\n\t\t\t\tconst headings = [];\n\t\t\t\tconst images = [];\n\t\t\t\tconst tables = [];\n\t\t\t\tconst lists = [];\n\t\t\t\tconst articles = [];\n\t\t\t\tcontainer.querySelectorAll(\"h1, h2, h3, h4, h5, h6\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) headings.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tcontainer.querySelectorAll(\"img\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) images.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tcontainer.querySelectorAll(\"table\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) tables.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tcontainer.querySelectorAll(\"ul, ol\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) lists.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tcontainer.querySelectorAll(\"article, [role=\\\"article\\\"]\").forEach((el) => {\n\t\t\t\t\tif (this.shouldIncludeElement(el, options)) {\n\t\t\t\t\t\tconst extracted = DOMTraversal.extractElement(el, options);\n\t\t\t\t\t\tif (extracted) articles.push(extracted);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\theadings,\n\t\t\t\t\timages,\n\t\t\t\t\ttables,\n\t\t\t\t\tlists,\n\t\t\t\t\tarticles\n\t\t\t\t};\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract metadata\n\t\t\t*/\n\t\t\textractMetadata(doc, container, options) {\n\t\t\t\tconst allElements = container.querySelectorAll(\"*\");\n\t\t\t\tconst extractedElements = container.querySelectorAll(\"button, a, input, textarea, select, h1, h2, h3, h4, h5, h6, img, table, ul, ol, article\").length;\n\t\t\t\tconst metadata = {\n\t\t\t\t\ttotalElements: allElements.length,\n\t\t\t\t\textractedElements\n\t\t\t\t};\n\t\t\t\tif (options.mainContentOnly && container instanceof Element) metadata.mainContent = SelectorGenerator.generateSelectors(container).css;\n\t\t\t\tconst language = doc.documentElement.getAttribute(\"lang\");\n\t\t\t\tif (language) metadata.language = language;\n\t\t\t\treturn metadata;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Check if element should be included based on options\n\t\t\t*/\n\t\t\tshouldIncludeElement(element, options) {\n\t\t\t\tif (!options.includeHidden && !DOMTraversal.isVisible(element)) return false;\n\t\t\t\tif (options.viewportOnly && !DOMTraversal.isInViewport(element)) return false;\n\t\t\t\tif (options.filter && !DOMTraversal.passesFilter(element, options.filter)) return false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Detect errors on the page\n\t\t\t*/\n\t\t\tdetectErrors(doc) {\n\t\t\t\treturn [\n\t\t\t\t\t\".error\",\n\t\t\t\t\t\".alert-danger\",\n\t\t\t\t\t\"[role=\\\"alert\\\"]\",\n\t\t\t\t\t\".error-message\"\n\t\t\t\t].some((sel) => {\n\t\t\t\t\tconst element = doc.querySelector(sel);\n\t\t\t\t\treturn element ? DOMTraversal.isVisible(element) : false;\n\t\t\t\t});\n\t\t\t}\n\t\t\t/**\n\t\t\t* Detect if page is loading\n\t\t\t*/\n\t\t\tdetectLoading(doc) {\n\t\t\t\treturn [\n\t\t\t\t\t\".loading\",\n\t\t\t\t\t\".spinner\",\n\t\t\t\t\t\"[aria-busy=\\\"true\\\"]\",\n\t\t\t\t\t\".loader\"\n\t\t\t\t].some((sel) => {\n\t\t\t\t\tconst element = doc.querySelector(sel);\n\t\t\t\t\treturn element ? DOMTraversal.isVisible(element) : false;\n\t\t\t\t});\n\t\t\t}\n\t\t\t/**\n\t\t\t* Detect modal dialogs\n\t\t\t*/\n\t\t\tdetectModals(doc) {\n\t\t\t\treturn [\n\t\t\t\t\t\"[role=\\\"dialog\\\"]\",\n\t\t\t\t\t\".modal\",\n\t\t\t\t\t\".popup\",\n\t\t\t\t\t\".overlay\"\n\t\t\t\t].some((sel) => {\n\t\t\t\t\tconst element = doc.querySelector(sel);\n\t\t\t\t\treturn element ? DOMTraversal.isVisible(element) : false;\n\t\t\t\t});\n\t\t\t}\n\t\t\t/**\n\t\t\t* Get currently focused element\n\t\t\t*/\n\t\t\tgetFocusedElement(doc) {\n\t\t\t\tconst focused = doc.activeElement;\n\t\t\t\tif (focused && focused !== doc.body) return SelectorGenerator.generateSelectors(focused).css;\n\t\t\t}\n\t\t\t/**\n\t\t\t* Quick extraction for interactive elements only\n\t\t\t* @param doc The document to extract from\n\t\t\t* @param options Extraction options\n\t\t\t*/\n\t\t\tstatic extractInteractive(doc, options = {}) {\n\t\t\t\treturn new SmartDOMReader({\n\t\t\t\t\t...options,\n\t\t\t\t\tmode: \"interactive\"\n\t\t\t\t}).extract(doc);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Quick extraction for full content\n\t\t\t* @param doc The document to extract from\n\t\t\t* @param options Extraction options\n\t\t\t*/\n\t\t\tstatic extractFull(doc, options = {}) {\n\t\t\t\treturn new SmartDOMReader({\n\t\t\t\t\t...options,\n\t\t\t\t\tmode: \"full\"\n\t\t\t\t}).extract(doc);\n\t\t\t}\n\t\t\t/**\n\t\t\t* Extract from a specific element\n\t\t\t* @param element The element to extract from\n\t\t\t* @param mode The extraction mode\n\t\t\t* @param options Additional options\n\t\t\t*/\n\t\t\tstatic extractFromElement(element, mode = \"interactive\", options = {}) {\n\t\t\t\treturn new SmartDOMReader({\n\t\t\t\t\t...options,\n\t\t\t\t\tmode\n\t\t\t\t}).extract(element);\n\t\t\t}\n\t\t};\n\t}));\n\t//#endregion\n\t//#region src/bundle-entry.ts\n\tinit_src();\n\tinit_markdown_formatter();\n\tinit_progressive();\n\tfunction executeExtraction(method, args) {\n\t\ttry {\n\t\t\tlet result;\n\t\t\tswitch (method) {\n\t\t\t\tcase \"extractStructure\": {\n\t\t\t\t\tconst { selector, frameSelector, formatOptions } = args;\n\t\t\t\t\tlet doc = document;\n\t\t\t\t\tif (frameSelector) {\n\t\t\t\t\t\tconst iframe = document.querySelector(frameSelector);\n\t\t\t\t\t\tif (!iframe || !(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return { error: `Cannot access iframe: ${frameSelector}` };\n\t\t\t\t\t\tdoc = iframe.contentDocument;\n\t\t\t\t\t}\n\t\t\t\t\tconst target = selector ? doc.querySelector(selector) ?? doc : doc;\n\t\t\t\t\tconst overview = ProgressiveExtractor.extractStructure(target);\n\t\t\t\t\tconst meta = {\n\t\t\t\t\t\ttitle: document.title,\n\t\t\t\t\t\turl: location.href\n\t\t\t\t\t};\n\t\t\t\t\tresult = MarkdownFormatter.structure(overview, formatOptions ?? { detail: \"summary\" }, meta);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"extractRegion\": {\n\t\t\t\t\tconst { selector, mode, frameSelector, options, formatOptions } = args;\n\t\t\t\t\tlet doc = document;\n\t\t\t\t\tif (frameSelector) {\n\t\t\t\t\t\tconst iframe = document.querySelector(frameSelector);\n\t\t\t\t\t\tif (!iframe || !(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return { error: `Cannot access iframe: ${frameSelector}` };\n\t\t\t\t\t\tdoc = iframe.contentDocument;\n\t\t\t\t\t}\n\t\t\t\t\tconst extractOptions = {\n\t\t\t\t\t\t...options || {},\n\t\t\t\t\t\tmode: mode || \"interactive\"\n\t\t\t\t\t};\n\t\t\t\t\tconst extractResult = ProgressiveExtractor.extractRegion(selector, doc, extractOptions, SmartDOMReader);\n\t\t\t\t\tif (!extractResult) return { error: `No element found matching selector: ${selector}` };\n\t\t\t\t\tconst meta = {\n\t\t\t\t\t\ttitle: document.title,\n\t\t\t\t\t\turl: location.href\n\t\t\t\t\t};\n\t\t\t\t\tresult = MarkdownFormatter.region(extractResult, formatOptions ?? { detail: \"region\" }, meta);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"extractContent\": {\n\t\t\t\t\tconst { selector, frameSelector, options, formatOptions } = args;\n\t\t\t\t\tlet doc = document;\n\t\t\t\t\tif (frameSelector) {\n\t\t\t\t\t\tconst iframe = document.querySelector(frameSelector);\n\t\t\t\t\t\tif (!iframe || !(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return { error: `Cannot access iframe: ${frameSelector}` };\n\t\t\t\t\t\tdoc = iframe.contentDocument;\n\t\t\t\t\t}\n\t\t\t\t\tconst extractOptions = options || {};\n\t\t\t\t\tconst extractResult = ProgressiveExtractor.extractContent(selector, doc, extractOptions);\n\t\t\t\t\tif (!extractResult) return { error: `No element found matching selector: ${selector}` };\n\t\t\t\t\tconst meta = {\n\t\t\t\t\t\ttitle: document.title,\n\t\t\t\t\t\turl: location.href\n\t\t\t\t\t};\n\t\t\t\t\tresult = MarkdownFormatter.content(extractResult, formatOptions ?? { detail: \"region\" }, meta);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"extractInteractive\": {\n\t\t\t\t\tconst { selector, frameSelector, options, formatOptions } = args;\n\t\t\t\t\tlet doc = document;\n\t\t\t\t\tif (frameSelector) {\n\t\t\t\t\t\tconst iframe = document.querySelector(frameSelector);\n\t\t\t\t\t\tif (!iframe || !(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return { error: `Cannot access iframe: ${frameSelector}` };\n\t\t\t\t\t\tdoc = iframe.contentDocument;\n\t\t\t\t\t}\n\t\t\t\t\tconst extractResult = selector ? SmartDOMReader.extractFromElement(doc.querySelector(selector), \"interactive\", options || {}) : SmartDOMReader.extractInteractive(doc, options || {});\n\t\t\t\t\tconst meta = {\n\t\t\t\t\t\ttitle: document.title,\n\t\t\t\t\t\turl: location.href\n\t\t\t\t\t};\n\t\t\t\t\tresult = MarkdownFormatter.region(extractResult, formatOptions ?? { detail: \"region\" }, meta);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"extractFull\": {\n\t\t\t\t\tconst { selector, frameSelector, options, formatOptions } = args;\n\t\t\t\t\tlet doc = document;\n\t\t\t\t\tif (frameSelector) {\n\t\t\t\t\t\tconst iframe = document.querySelector(frameSelector);\n\t\t\t\t\t\tif (!iframe || !(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return { error: `Cannot access iframe: ${frameSelector}` };\n\t\t\t\t\t\tdoc = iframe.contentDocument;\n\t\t\t\t\t}\n\t\t\t\t\tconst extractResult = selector ? SmartDOMReader.extractFromElement(doc.querySelector(selector), \"full\", options || {}) : SmartDOMReader.extractFull(doc, options || {});\n\t\t\t\t\tconst meta = {\n\t\t\t\t\t\ttitle: document.title,\n\t\t\t\t\t\turl: location.href\n\t\t\t\t\t};\n\t\t\t\t\tresult = MarkdownFormatter.region(extractResult, formatOptions ?? { detail: \"deep\" }, meta);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault: return { error: `Unknown method: ${method}` };\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\treturn { error: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\t//#endregion\n\texports.SmartDOMReaderBundle = { executeExtraction };\n\texports.executeExtraction = executeExtraction;\n\treturn exports;\n})({});\n";
10
+ declare const SMART_DOM_READER_VERSION = "1.0.0";
11
+ //#endregion
12
+ export { SMART_DOM_READER_BUNDLE, SMART_DOM_READER_VERSION };
13
+ //# sourceMappingURL=bundle-string.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-string.d.mts","names":[],"sources":["../src/bundle-string.ts"],"mappings":";;AAQA;;;;;AAEA;cAFa,uBAAA;AAAA,cAEA,wBAAA"}