webmcp-cli 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/dist/analysis/form-to-tool-mapper.d.ts +61 -0
  2. package/dist/analysis/form-to-tool-mapper.js +360 -0
  3. package/dist/analysis/form-to-tool-mapper.js.map +1 -0
  4. package/dist/analysis/index.d.ts +84 -0
  5. package/dist/analysis/index.js +81 -0
  6. package/dist/analysis/index.js.map +1 -0
  7. package/dist/analysis/missing-tool-analyzer.d.ts +35 -0
  8. package/dist/analysis/missing-tool-analyzer.js +617 -0
  9. package/dist/analysis/missing-tool-analyzer.js.map +1 -0
  10. package/dist/audit/run-multi-page-audit.d.ts +34 -0
  11. package/dist/audit/run-multi-page-audit.js +233 -0
  12. package/dist/audit/run-multi-page-audit.js.map +1 -0
  13. package/dist/cli/commands/potential.d.ts +8 -0
  14. package/dist/cli/commands/potential.js +323 -0
  15. package/dist/cli/commands/potential.js.map +1 -0
  16. package/dist/cli/commands/report.d.ts +12 -0
  17. package/dist/cli/commands/report.js +89 -0
  18. package/dist/cli/commands/report.js.map +1 -0
  19. package/dist/cli/index.js +35 -0
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/config/defaults.d.ts +36 -0
  22. package/dist/config/defaults.js +33 -0
  23. package/dist/config/defaults.js.map +1 -0
  24. package/dist/config/index.d.ts +7 -0
  25. package/dist/config/index.js +7 -0
  26. package/dist/config/index.js.map +1 -0
  27. package/dist/config/loader.d.ts +22 -0
  28. package/dist/config/loader.js +91 -0
  29. package/dist/config/loader.js.map +1 -0
  30. package/dist/config/schema.d.ts +280 -0
  31. package/dist/config/schema.js +42 -0
  32. package/dist/config/schema.js.map +1 -0
  33. package/dist/core/types/audit.d.ts +1 -1
  34. package/dist/core/types/index.d.ts +1 -0
  35. package/dist/core/types/index.js +1 -0
  36. package/dist/core/types/index.js.map +1 -1
  37. package/dist/core/types/recon.d.ts +265 -0
  38. package/dist/core/types/recon.js +5 -0
  39. package/dist/core/types/recon.js.map +1 -0
  40. package/dist/core/types/rule.d.ts +1 -1
  41. package/dist/core/types/rule.js +7 -5
  42. package/dist/core/types/rule.js.map +1 -1
  43. package/dist/crawler/depth-crawler.d.ts +29 -0
  44. package/dist/crawler/depth-crawler.js +212 -0
  45. package/dist/crawler/depth-crawler.js.map +1 -0
  46. package/dist/crawler/index.d.ts +2 -0
  47. package/dist/crawler/index.js +3 -0
  48. package/dist/crawler/index.js.map +1 -0
  49. package/dist/crawler/link-extractor.d.ts +1 -0
  50. package/dist/crawler/link-extractor.js +49 -0
  51. package/dist/crawler/link-extractor.js.map +1 -0
  52. package/dist/generators/index.d.ts +10 -0
  53. package/dist/generators/index.js +8 -0
  54. package/dist/generators/index.js.map +1 -0
  55. package/dist/generators/report-html.d.ts +12 -0
  56. package/dist/generators/report-html.js +470 -0
  57. package/dist/generators/report-html.js.map +1 -0
  58. package/dist/generators/report-json.d.ts +95 -0
  59. package/dist/generators/report-json.js +144 -0
  60. package/dist/generators/report-json.js.map +1 -0
  61. package/dist/generators/report-manager.d.ts +31 -0
  62. package/dist/generators/report-manager.js +208 -0
  63. package/dist/generators/report-manager.js.map +1 -0
  64. package/dist/generators/tool-code-generator.d.ts +31 -0
  65. package/dist/generators/tool-code-generator.js +201 -0
  66. package/dist/generators/tool-code-generator.js.map +1 -0
  67. package/dist/potential/ai-recommender.d.ts +33 -0
  68. package/dist/potential/ai-recommender.js +414 -0
  69. package/dist/potential/ai-recommender.js.map +1 -0
  70. package/dist/potential/analyzer.d.ts +32 -0
  71. package/dist/potential/analyzer.js +383 -0
  72. package/dist/potential/analyzer.js.map +1 -0
  73. package/dist/potential/index.d.ts +3 -0
  74. package/dist/potential/index.js +4 -0
  75. package/dist/potential/index.js.map +1 -0
  76. package/dist/potential/prompts.d.ts +20 -0
  77. package/dist/potential/prompts.js +42 -0
  78. package/dist/potential/prompts.js.map +1 -0
  79. package/dist/potential/types.d.ts +40 -0
  80. package/dist/potential/types.js +2 -0
  81. package/dist/potential/types.js.map +1 -0
  82. package/dist/recon/index.d.ts +20 -0
  83. package/dist/recon/index.js +143 -0
  84. package/dist/recon/index.js.map +1 -0
  85. package/dist/recon/manifest.d.ts +16 -0
  86. package/dist/recon/manifest.js +108 -0
  87. package/dist/recon/manifest.js.map +1 -0
  88. package/dist/recon/meta-extractor.d.ts +11 -0
  89. package/dist/recon/meta-extractor.js +276 -0
  90. package/dist/recon/meta-extractor.js.map +1 -0
  91. package/dist/recon/robots.d.ts +16 -0
  92. package/dist/recon/robots.js +158 -0
  93. package/dist/recon/robots.js.map +1 -0
  94. package/dist/recon/route-discovery.d.ts +25 -0
  95. package/dist/recon/route-discovery.js +303 -0
  96. package/dist/recon/route-discovery.js.map +1 -0
  97. package/dist/recon/sitemap.d.ts +12 -0
  98. package/dist/recon/sitemap.js +177 -0
  99. package/dist/recon/sitemap.js.map +1 -0
  100. package/dist/rules/accessibility/AXE-001.d.ts +9 -0
  101. package/dist/rules/accessibility/AXE-001.js +109 -0
  102. package/dist/rules/accessibility/AXE-001.js.map +1 -0
  103. package/dist/rules/accessibility/AXE-002.d.ts +8 -0
  104. package/dist/rules/accessibility/AXE-002.js +85 -0
  105. package/dist/rules/accessibility/AXE-002.js.map +1 -0
  106. package/dist/rules/accessibility/AXE-003.d.ts +8 -0
  107. package/dist/rules/accessibility/AXE-003.js +94 -0
  108. package/dist/rules/accessibility/AXE-003.js.map +1 -0
  109. package/dist/rules/accessibility/AXE-004.d.ts +8 -0
  110. package/dist/rules/accessibility/AXE-004.js +101 -0
  111. package/dist/rules/accessibility/AXE-004.js.map +1 -0
  112. package/dist/rules/accessibility/AXE-005.d.ts +9 -0
  113. package/dist/rules/accessibility/AXE-005.js +89 -0
  114. package/dist/rules/accessibility/AXE-005.js.map +1 -0
  115. package/dist/rules/best-practices/BP-004.d.ts +9 -0
  116. package/dist/rules/best-practices/BP-004.js +96 -0
  117. package/dist/rules/best-practices/BP-004.js.map +1 -0
  118. package/dist/rules/best-practices/BP-005.d.ts +8 -0
  119. package/dist/rules/best-practices/BP-005.js +94 -0
  120. package/dist/rules/best-practices/BP-005.js.map +1 -0
  121. package/dist/rules/best-practices/BP-006.d.ts +8 -0
  122. package/dist/rules/best-practices/BP-006.js +80 -0
  123. package/dist/rules/best-practices/BP-006.js.map +1 -0
  124. package/dist/rules/best-practices/BP-007.d.ts +8 -0
  125. package/dist/rules/best-practices/BP-007.js +92 -0
  126. package/dist/rules/best-practices/BP-007.js.map +1 -0
  127. package/dist/rules/best-practices/BP-008.d.ts +12 -0
  128. package/dist/rules/best-practices/BP-008.js +86 -0
  129. package/dist/rules/best-practices/BP-008.js.map +1 -0
  130. package/dist/rules/best-practices/BP-009.d.ts +9 -0
  131. package/dist/rules/best-practices/BP-009.js +77 -0
  132. package/dist/rules/best-practices/BP-009.js.map +1 -0
  133. package/dist/rules/best-practices/BP-010.d.ts +8 -0
  134. package/dist/rules/best-practices/BP-010.js +85 -0
  135. package/dist/rules/best-practices/BP-010.js.map +1 -0
  136. package/dist/rules/coverage/COV-002.d.ts +8 -0
  137. package/dist/rules/coverage/COV-002.js +68 -0
  138. package/dist/rules/coverage/COV-002.js.map +1 -0
  139. package/dist/rules/coverage/COV-003.d.ts +8 -0
  140. package/dist/rules/coverage/COV-003.js +68 -0
  141. package/dist/rules/coverage/COV-003.js.map +1 -0
  142. package/dist/rules/coverage/COV-004.d.ts +8 -0
  143. package/dist/rules/coverage/COV-004.js +89 -0
  144. package/dist/rules/coverage/COV-004.js.map +1 -0
  145. package/dist/rules/coverage/COV-005.d.ts +8 -0
  146. package/dist/rules/coverage/COV-005.js +67 -0
  147. package/dist/rules/coverage/COV-005.js.map +1 -0
  148. package/dist/rules/coverage/COV-006.d.ts +9 -0
  149. package/dist/rules/coverage/COV-006.js +76 -0
  150. package/dist/rules/coverage/COV-006.js.map +1 -0
  151. package/dist/rules/coverage/COV-007.d.ts +8 -0
  152. package/dist/rules/coverage/COV-007.js +67 -0
  153. package/dist/rules/coverage/COV-007.js.map +1 -0
  154. package/dist/rules/coverage/COV-008.d.ts +9 -0
  155. package/dist/rules/coverage/COV-008.js +87 -0
  156. package/dist/rules/coverage/COV-008.js.map +1 -0
  157. package/dist/rules/coverage/COV-009.d.ts +8 -0
  158. package/dist/rules/coverage/COV-009.js +73 -0
  159. package/dist/rules/coverage/COV-009.js.map +1 -0
  160. package/dist/rules/coverage/COV-010.d.ts +9 -0
  161. package/dist/rules/coverage/COV-010.js +82 -0
  162. package/dist/rules/coverage/COV-010.js.map +1 -0
  163. package/dist/rules/description/DESC-001.d.ts +9 -0
  164. package/dist/rules/description/DESC-001.js +88 -0
  165. package/dist/rules/description/DESC-001.js.map +1 -0
  166. package/dist/rules/description/DESC-002.d.ts +10 -0
  167. package/dist/rules/description/DESC-002.js +99 -0
  168. package/dist/rules/description/DESC-002.js.map +1 -0
  169. package/dist/rules/description/DESC-006.d.ts +9 -0
  170. package/dist/rules/description/DESC-006.js +78 -0
  171. package/dist/rules/description/DESC-006.js.map +1 -0
  172. package/dist/rules/description/DESC-007.d.ts +9 -0
  173. package/dist/rules/description/DESC-007.js +70 -0
  174. package/dist/rules/description/DESC-007.js.map +1 -0
  175. package/dist/rules/description/DESC-008.d.ts +9 -0
  176. package/dist/rules/description/DESC-008.js +70 -0
  177. package/dist/rules/description/DESC-008.js.map +1 -0
  178. package/dist/rules/description/DESC-009.d.ts +8 -0
  179. package/dist/rules/description/DESC-009.js +55 -0
  180. package/dist/rules/description/DESC-009.js.map +1 -0
  181. package/dist/rules/description/DESC-010.d.ts +9 -0
  182. package/dist/rules/description/DESC-010.js +92 -0
  183. package/dist/rules/description/DESC-010.js.map +1 -0
  184. package/dist/rules/description/DESC-011.d.ts +9 -0
  185. package/dist/rules/description/DESC-011.js +81 -0
  186. package/dist/rules/description/DESC-011.js.map +1 -0
  187. package/dist/rules/description/DESC-012.d.ts +9 -0
  188. package/dist/rules/description/DESC-012.js +98 -0
  189. package/dist/rules/description/DESC-012.js.map +1 -0
  190. package/dist/rules/implementation/IMP-002.d.ts +9 -0
  191. package/dist/rules/implementation/IMP-002.js +59 -0
  192. package/dist/rules/implementation/IMP-002.js.map +1 -0
  193. package/dist/rules/implementation/IMP-006.d.ts +9 -0
  194. package/dist/rules/implementation/IMP-006.js +48 -0
  195. package/dist/rules/implementation/IMP-006.js.map +1 -0
  196. package/dist/rules/implementation/IMP-008.d.ts +9 -0
  197. package/dist/rules/implementation/IMP-008.js +46 -0
  198. package/dist/rules/implementation/IMP-008.js.map +1 -0
  199. package/dist/rules/implementation/IMP-009.d.ts +9 -0
  200. package/dist/rules/implementation/IMP-009.js +48 -0
  201. package/dist/rules/implementation/IMP-009.js.map +1 -0
  202. package/dist/rules/implementation/IMP-010.d.ts +9 -0
  203. package/dist/rules/implementation/IMP-010.js +66 -0
  204. package/dist/rules/implementation/IMP-010.js.map +1 -0
  205. package/dist/rules/implementation/IMP-011.d.ts +9 -0
  206. package/dist/rules/implementation/IMP-011.js +82 -0
  207. package/dist/rules/implementation/IMP-011.js.map +1 -0
  208. package/dist/rules/implementation/IMP-012.d.ts +9 -0
  209. package/dist/rules/implementation/IMP-012.js +88 -0
  210. package/dist/rules/implementation/IMP-012.js.map +1 -0
  211. package/dist/rules/implementation/IMP-014.d.ts +9 -0
  212. package/dist/rules/implementation/IMP-014.js +58 -0
  213. package/dist/rules/implementation/IMP-014.js.map +1 -0
  214. package/dist/rules/implementation/IMP-015.d.ts +9 -0
  215. package/dist/rules/implementation/IMP-015.js +64 -0
  216. package/dist/rules/implementation/IMP-015.js.map +1 -0
  217. package/dist/rules/implementation/IMP-016.d.ts +9 -0
  218. package/dist/rules/implementation/IMP-016.js +52 -0
  219. package/dist/rules/implementation/IMP-016.js.map +1 -0
  220. package/dist/rules/implementation/IMP-017.d.ts +8 -0
  221. package/dist/rules/implementation/IMP-017.js +51 -0
  222. package/dist/rules/implementation/IMP-017.js.map +1 -0
  223. package/dist/rules/implementation/IMP-018.d.ts +8 -0
  224. package/dist/rules/implementation/IMP-018.js +52 -0
  225. package/dist/rules/implementation/IMP-018.js.map +1 -0
  226. package/dist/rules/implementation/IMP-019.d.ts +8 -0
  227. package/dist/rules/implementation/IMP-019.js +53 -0
  228. package/dist/rules/implementation/IMP-019.js.map +1 -0
  229. package/dist/rules/implementation/IMP-020.d.ts +9 -0
  230. package/dist/rules/implementation/IMP-020.js +62 -0
  231. package/dist/rules/implementation/IMP-020.js.map +1 -0
  232. package/dist/rules/implementation/IMP-021.d.ts +8 -0
  233. package/dist/rules/implementation/IMP-021.js +64 -0
  234. package/dist/rules/implementation/IMP-021.js.map +1 -0
  235. package/dist/rules/implementation/IMP-022.d.ts +8 -0
  236. package/dist/rules/implementation/IMP-022.js +70 -0
  237. package/dist/rules/implementation/IMP-022.js.map +1 -0
  238. package/dist/rules/index.d.ts +73 -6
  239. package/dist/rules/index.js +141 -6
  240. package/dist/rules/index.js.map +1 -1
  241. package/dist/rules/schema/SCHEMA-004.d.ts +9 -0
  242. package/dist/rules/schema/SCHEMA-004.js +57 -0
  243. package/dist/rules/schema/SCHEMA-004.js.map +1 -0
  244. package/dist/rules/schema/SCHEMA-005.d.ts +9 -0
  245. package/dist/rules/schema/SCHEMA-005.js +61 -0
  246. package/dist/rules/schema/SCHEMA-005.js.map +1 -0
  247. package/dist/rules/schema/SCHEMA-006.d.ts +10 -0
  248. package/dist/rules/schema/SCHEMA-006.js +85 -0
  249. package/dist/rules/schema/SCHEMA-006.js.map +1 -0
  250. package/dist/rules/schema/SCHEMA-007.d.ts +9 -0
  251. package/dist/rules/schema/SCHEMA-007.js +73 -0
  252. package/dist/rules/schema/SCHEMA-007.js.map +1 -0
  253. package/dist/rules/schema/SCHEMA-008.d.ts +9 -0
  254. package/dist/rules/schema/SCHEMA-008.js +70 -0
  255. package/dist/rules/schema/SCHEMA-008.js.map +1 -0
  256. package/dist/rules/schema/SCHEMA-009.d.ts +10 -0
  257. package/dist/rules/schema/SCHEMA-009.js +80 -0
  258. package/dist/rules/schema/SCHEMA-009.js.map +1 -0
  259. package/dist/rules/schema/SCHEMA-010.d.ts +9 -0
  260. package/dist/rules/schema/SCHEMA-010.js +96 -0
  261. package/dist/rules/schema/SCHEMA-010.js.map +1 -0
  262. package/dist/rules/schema/SCHEMA-012.d.ts +9 -0
  263. package/dist/rules/schema/SCHEMA-012.js +65 -0
  264. package/dist/rules/schema/SCHEMA-012.js.map +1 -0
  265. package/dist/rules/security/SEC-002.d.ts +8 -0
  266. package/dist/rules/security/SEC-002.js +81 -0
  267. package/dist/rules/security/SEC-002.js.map +1 -0
  268. package/dist/rules/security/SEC-003.d.ts +8 -0
  269. package/dist/rules/security/SEC-003.js +85 -0
  270. package/dist/rules/security/SEC-003.js.map +1 -0
  271. package/dist/rules/security/SEC-004.d.ts +9 -0
  272. package/dist/rules/security/SEC-004.js +87 -0
  273. package/dist/rules/security/SEC-004.js.map +1 -0
  274. package/dist/rules/security/SEC-005.d.ts +8 -0
  275. package/dist/rules/security/SEC-005.js +87 -0
  276. package/dist/rules/security/SEC-005.js.map +1 -0
  277. package/dist/rules/security/SEC-006.d.ts +10 -0
  278. package/dist/rules/security/SEC-006.js +108 -0
  279. package/dist/rules/security/SEC-006.js.map +1 -0
  280. package/dist/rules/security/SEC-007.d.ts +9 -0
  281. package/dist/rules/security/SEC-007.js +108 -0
  282. package/dist/rules/security/SEC-007.js.map +1 -0
  283. package/dist/rules/security/SEC-008.d.ts +8 -0
  284. package/dist/rules/security/SEC-008.js +109 -0
  285. package/dist/rules/security/SEC-008.js.map +1 -0
  286. package/dist/rules/security/SEC-009.d.ts +9 -0
  287. package/dist/rules/security/SEC-009.js +93 -0
  288. package/dist/rules/security/SEC-009.js.map +1 -0
  289. package/dist/rules/security/SEC-010.d.ts +8 -0
  290. package/dist/rules/security/SEC-010.js +78 -0
  291. package/dist/rules/security/SEC-010.js.map +1 -0
  292. package/dist/rules/security/SEC-011.d.ts +8 -0
  293. package/dist/rules/security/SEC-011.js +93 -0
  294. package/dist/rules/security/SEC-011.js.map +1 -0
  295. package/dist/rules/security/SEC-012.d.ts +8 -0
  296. package/dist/rules/security/SEC-012.js +79 -0
  297. package/dist/rules/security/SEC-012.js.map +1 -0
  298. package/dist/rules/security/SEC-013.d.ts +9 -0
  299. package/dist/rules/security/SEC-013.js +107 -0
  300. package/dist/rules/security/SEC-013.js.map +1 -0
  301. package/dist/scoring/calculator.js +1 -0
  302. package/dist/scoring/calculator.js.map +1 -1
  303. package/dist/ui/ink/components/AIRecommendationCard.d.ts +11 -0
  304. package/dist/ui/ink/components/AIRecommendationCard.js +23 -0
  305. package/dist/ui/ink/components/AIRecommendationCard.js.map +1 -0
  306. package/dist/ui/ink/components/OpportunityList.d.ts +10 -0
  307. package/dist/ui/ink/components/OpportunityList.js +48 -0
  308. package/dist/ui/ink/components/OpportunityList.js.map +1 -0
  309. package/dist/ui/ink/components/PotentialPageCard.d.ts +13 -0
  310. package/dist/ui/ink/components/PotentialPageCard.js +43 -0
  311. package/dist/ui/ink/components/PotentialPageCard.js.map +1 -0
  312. package/dist/ui/ink/components/PotentialProgress.d.ts +16 -0
  313. package/dist/ui/ink/components/PotentialProgress.js +44 -0
  314. package/dist/ui/ink/components/PotentialProgress.js.map +1 -0
  315. package/dist/ui/ink/components/PotentialSummary.d.ts +10 -0
  316. package/dist/ui/ink/components/PotentialSummary.js +86 -0
  317. package/dist/ui/ink/components/PotentialSummary.js.map +1 -0
  318. package/dist/ui/ink/components/SuggestionCard.d.ts +34 -0
  319. package/dist/ui/ink/components/SuggestionCard.js +36 -0
  320. package/dist/ui/ink/components/SuggestionCard.js.map +1 -0
  321. package/dist/ui/ink/components/views/MultiPageCrawlView.d.ts +21 -0
  322. package/dist/ui/ink/components/views/MultiPageCrawlView.js +55 -0
  323. package/dist/ui/ink/components/views/MultiPageCrawlView.js.map +1 -0
  324. package/dist/ui/ink/components/views/PotentialView.d.ts +18 -0
  325. package/dist/ui/ink/components/views/PotentialView.js +74 -0
  326. package/dist/ui/ink/components/views/PotentialView.js.map +1 -0
  327. package/dist/ui/ink/components/views/ReconView.d.ts +22 -0
  328. package/dist/ui/ink/components/views/ReconView.js +30 -0
  329. package/dist/ui/ink/components/views/ReconView.js.map +1 -0
  330. package/package.json +2 -1
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Sitemap Parser
3
+ *
4
+ * Fetches and parses sitemap.xml and sitemap index files.
5
+ * Uses native fetch. Handles missing/malformed sitemaps gracefully.
6
+ */
7
+ /** Maximum number of sitemap index children to follow */
8
+ const MAX_SITEMAP_INDEX_CHILDREN = 10;
9
+ /** Maximum entries to collect across all sitemaps */
10
+ const MAX_ENTRIES = 500;
11
+ /** Fetch timeout for each sitemap request (ms) */
12
+ const FETCH_TIMEOUT_MS = 10_000;
13
+ /**
14
+ * Fetch with timeout using AbortController
15
+ */
16
+ async function fetchWithTimeout(url, timeoutMs = FETCH_TIMEOUT_MS) {
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
19
+ try {
20
+ return await fetch(url, {
21
+ signal: controller.signal,
22
+ headers: { 'User-Agent': 'WebMCP-CLI/1.0 (sitemap-parser)' },
23
+ redirect: 'follow',
24
+ });
25
+ }
26
+ finally {
27
+ clearTimeout(timer);
28
+ }
29
+ }
30
+ /**
31
+ * Naive XML tag content extractor (avoids needing an XML parser dependency).
32
+ * Returns all text content between <tagName> and </tagName>.
33
+ */
34
+ function extractTagValues(xml, tagName) {
35
+ const results = [];
36
+ const openTag = `<${tagName}`;
37
+ const closeTag = `</${tagName}>`;
38
+ let start = 0;
39
+ while (true) {
40
+ const openIdx = xml.indexOf(openTag, start);
41
+ if (openIdx === -1)
42
+ break;
43
+ // Find the end of the opening tag (handles attributes)
44
+ const openEnd = xml.indexOf('>', openIdx);
45
+ if (openEnd === -1)
46
+ break;
47
+ const closeIdx = xml.indexOf(closeTag, openEnd);
48
+ if (closeIdx === -1)
49
+ break;
50
+ const content = xml.substring(openEnd + 1, closeIdx).trim();
51
+ if (content) {
52
+ results.push(content);
53
+ }
54
+ start = closeIdx + closeTag.length;
55
+ }
56
+ return results;
57
+ }
58
+ /**
59
+ * Extract sitemap entries from XML content
60
+ */
61
+ function parseSitemapXml(xml) {
62
+ const entries = [];
63
+ // Split by <url> blocks
64
+ const urlBlocks = xml.split(/<url\b[^>]*>/i).slice(1);
65
+ for (const block of urlBlocks) {
66
+ const endIdx = block.indexOf('</url>');
67
+ const urlBlock = endIdx !== -1 ? block.substring(0, endIdx) : block;
68
+ const locs = extractTagValues(urlBlock, 'loc');
69
+ const loc = locs[0];
70
+ if (!loc)
71
+ continue;
72
+ const lastmods = extractTagValues(urlBlock, 'lastmod');
73
+ const changefreqs = extractTagValues(urlBlock, 'changefreq');
74
+ const priorities = extractTagValues(urlBlock, 'priority');
75
+ entries.push({
76
+ loc,
77
+ lastmod: lastmods[0],
78
+ changefreq: changefreqs[0],
79
+ priority: priorities[0] ? parseFloat(priorities[0]) : undefined,
80
+ });
81
+ if (entries.length >= MAX_ENTRIES)
82
+ break;
83
+ }
84
+ return entries;
85
+ }
86
+ /**
87
+ * Check if XML content is a sitemap index
88
+ */
89
+ function isSitemapIndex(xml) {
90
+ return xml.includes('<sitemapindex') || xml.includes('<sitemapindex>');
91
+ }
92
+ /**
93
+ * Extract child sitemap URLs from a sitemap index
94
+ */
95
+ function extractIndexUrls(xml) {
96
+ // Split by <sitemap> blocks and extract <loc> from each
97
+ const sitemapBlocks = xml.split(/<sitemap\b[^>]*>/i).slice(1);
98
+ const urls = [];
99
+ for (const block of sitemapBlocks) {
100
+ const locs = extractTagValues(block, 'loc');
101
+ if (locs[0]) {
102
+ urls.push(locs[0]);
103
+ }
104
+ if (urls.length >= MAX_SITEMAP_INDEX_CHILDREN)
105
+ break;
106
+ }
107
+ return urls;
108
+ }
109
+ /**
110
+ * Fetch and parse a sitemap from a URL.
111
+ * Handles sitemap index files by recursively fetching children.
112
+ */
113
+ export async function parseSitemap(baseUrl, sitemapUrl) {
114
+ const url = sitemapUrl ?? new URL('/sitemap.xml', baseUrl).href;
115
+ try {
116
+ const response = await fetchWithTimeout(url);
117
+ if (!response.ok) {
118
+ return {
119
+ found: false,
120
+ entries: [],
121
+ source: url,
122
+ isIndex: false,
123
+ error: `HTTP ${response.status}`,
124
+ };
125
+ }
126
+ const xml = await response.text();
127
+ if (isSitemapIndex(xml)) {
128
+ // Parse sitemap index — fetch children
129
+ const childUrls = extractIndexUrls(xml);
130
+ const allEntries = [];
131
+ const childResults = await Promise.allSettled(childUrls.map(async (childUrl) => {
132
+ try {
133
+ const childResp = await fetchWithTimeout(childUrl);
134
+ if (!childResp.ok)
135
+ return [];
136
+ const childXml = await childResp.text();
137
+ return parseSitemapXml(childXml);
138
+ }
139
+ catch {
140
+ return [];
141
+ }
142
+ }));
143
+ for (const result of childResults) {
144
+ if (result.status === 'fulfilled') {
145
+ allEntries.push(...result.value);
146
+ if (allEntries.length >= MAX_ENTRIES)
147
+ break;
148
+ }
149
+ }
150
+ return {
151
+ found: true,
152
+ entries: allEntries.slice(0, MAX_ENTRIES),
153
+ source: url,
154
+ isIndex: true,
155
+ };
156
+ }
157
+ // Regular sitemap
158
+ const entries = parseSitemapXml(xml);
159
+ return {
160
+ found: entries.length > 0,
161
+ entries,
162
+ source: url,
163
+ isIndex: false,
164
+ };
165
+ }
166
+ catch (error) {
167
+ const message = error instanceof Error ? error.message : 'Unknown error';
168
+ return {
169
+ found: false,
170
+ entries: [],
171
+ source: url,
172
+ isIndex: false,
173
+ error: message,
174
+ };
175
+ }
176
+ }
177
+ //# sourceMappingURL=sitemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../../src/recon/sitemap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AXE-001: Declarative Label Quality
3
+ *
4
+ * Checks that declarative form tools have good toolparamdescription
5
+ * values derived from labels. Labels should be descriptive enough
6
+ * for agents to understand what each parameter expects.
7
+ */
8
+ import type { Rule } from '../../core/types/rule.js';
9
+ export declare const AXE_001: Rule;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * AXE-001: Declarative Label Quality
3
+ *
4
+ * Checks that declarative form tools have good toolparamdescription
5
+ * values derived from labels. Labels should be descriptive enough
6
+ * for agents to understand what each parameter expects.
7
+ */
8
+ import { createRuleResult } from '../runner.js';
9
+ const MIN_LABEL_LENGTH = 3;
10
+ const VAGUE_LABELS = [
11
+ /^input$/i,
12
+ /^field$/i,
13
+ /^value$/i,
14
+ /^text$/i,
15
+ /^data$/i,
16
+ /^enter$/i,
17
+ /^type$/i,
18
+ /^here$/i,
19
+ /^\*$/,
20
+ /^\.+$/,
21
+ ];
22
+ export const AXE_001 = {
23
+ id: 'AXE-001',
24
+ category: 'accessibility',
25
+ name: 'Declarative Label Quality',
26
+ description: 'Form labels should provide good toolparamdescription values for agent understanding',
27
+ severity: 'warning',
28
+ maxScore: 8,
29
+ async check(context) {
30
+ const declarativeTools = context.tools.filter((t) => t.source === 'declarative');
31
+ if (declarativeTools.length === 0) {
32
+ return createRuleResult('AXE-001', 8, {
33
+ passed: true,
34
+ score: 8,
35
+ message: 'No declarative tools detected (rule not applicable)',
36
+ });
37
+ }
38
+ const html = context.html ?? '';
39
+ if (html.length === 0) {
40
+ return createRuleResult('AXE-001', 8, {
41
+ passed: true,
42
+ score: 8,
43
+ message: 'No HTML available for analysis',
44
+ });
45
+ }
46
+ const violations = [];
47
+ // Parse form fields from HTML
48
+ const formRegex = /<form[^>]*toolname\s*=\s*["']([^"']+)["'][^>]*>([\s\S]*?)<\/form>/gi;
49
+ let formMatch;
50
+ while ((formMatch = formRegex.exec(html)) !== null) {
51
+ const toolname = formMatch[1] ?? '';
52
+ const formBody = formMatch[2] ?? '';
53
+ // Find inputs without toolparamdescription
54
+ const inputRegex = /<(?:input|select|textarea)[^>]*name\s*=\s*["']([^"']+)["'][^>]*>/gi;
55
+ let inputMatch;
56
+ while ((inputMatch = inputRegex.exec(formBody)) !== null) {
57
+ const paramName = inputMatch[1] ?? '';
58
+ const inputTag = inputMatch[0];
59
+ // Skip hidden inputs
60
+ if (/type\s*=\s*["']hidden["']/i.test(inputTag))
61
+ continue;
62
+ // Check for toolparamdescription
63
+ const descMatch = inputTag.match(/toolparamdescription\s*=\s*["']([^"']*?)["']/i);
64
+ const desc = descMatch?.[1] ?? '';
65
+ // Check label text as fallback
66
+ const labelRegex = new RegExp(`<label[^>]*for\\s*=\\s*["']${paramName}["'][^>]*>([^<]*)</label>`, 'i');
67
+ const labelMatch = formBody.match(labelRegex);
68
+ const labelText = labelMatch?.[1]?.trim() ?? '';
69
+ const effectiveDesc = desc || labelText;
70
+ if (effectiveDesc.length < MIN_LABEL_LENGTH) {
71
+ violations.push({
72
+ tool: toolname,
73
+ param: paramName,
74
+ issue: `No description or label (length: ${effectiveDesc.length})`,
75
+ });
76
+ }
77
+ else if (VAGUE_LABELS.some((p) => p.test(effectiveDesc))) {
78
+ violations.push({
79
+ tool: toolname,
80
+ param: paramName,
81
+ issue: `Label "${effectiveDesc}" is too vague`,
82
+ });
83
+ }
84
+ }
85
+ }
86
+ if (violations.length === 0) {
87
+ return createRuleResult('AXE-001', 8, {
88
+ passed: true,
89
+ score: 8,
90
+ message: 'All declarative form parameters have good labels',
91
+ });
92
+ }
93
+ const penalty = Math.min(violations.length * 2, 8);
94
+ const score = Math.max(0, 8 - penalty);
95
+ return createRuleResult('AXE-001', 8, {
96
+ passed: false,
97
+ score,
98
+ message: `${violations.length} form parameter(s) have poor label quality`,
99
+ details: violations.map((v) => `Tool "${v.tool}" param "${v.param}": ${v.issue}`),
100
+ suggestions: [
101
+ 'Add toolparamdescription attributes to form inputs for clear agent guidance',
102
+ 'Use descriptive labels that explain what the field expects',
103
+ 'Example: <input name="email" toolparamdescription="User email address for account recovery">',
104
+ ],
105
+ affectedTools: [...new Set(violations.map((v) => v.tool))],
106
+ });
107
+ },
108
+ };
109
+ //# sourceMappingURL=AXE-001.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AXE-001.js","sourceRoot":"","sources":["../../../src/rules/accessibility/AXE-001.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * AXE-002: ARIA Descriptions Used
3
+ *
4
+ * Checks if ARIA descriptions on form fields enhance the
5
+ * toolparamdescription values provided to agents.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const AXE_002: Rule;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * AXE-002: ARIA Descriptions Used
3
+ *
4
+ * Checks if ARIA descriptions on form fields enhance the
5
+ * toolparamdescription values provided to agents.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ export const AXE_002 = {
9
+ id: 'AXE-002',
10
+ category: 'accessibility',
11
+ name: 'ARIA Descriptions Used',
12
+ description: 'ARIA descriptions on form fields should enhance parameter descriptions',
13
+ severity: 'info',
14
+ maxScore: 5,
15
+ async check(context) {
16
+ const declarativeTools = context.tools.filter((t) => t.source === 'declarative');
17
+ if (declarativeTools.length === 0) {
18
+ return createRuleResult('AXE-002', 5, {
19
+ passed: true,
20
+ score: 5,
21
+ message: 'No declarative tools detected (rule not applicable)',
22
+ });
23
+ }
24
+ const html = context.html ?? '';
25
+ if (html.length === 0) {
26
+ return createRuleResult('AXE-002', 5, {
27
+ passed: true,
28
+ score: 5,
29
+ message: 'No HTML available for analysis',
30
+ });
31
+ }
32
+ // Count fields with and without ARIA descriptions in toolname forms
33
+ const formRegex = /<form[^>]*toolname\s*=\s*["'][^"']+["'][^>]*>([\s\S]*?)<\/form>/gi;
34
+ let totalFields = 0;
35
+ let fieldsWithAria = 0;
36
+ let formMatch;
37
+ while ((formMatch = formRegex.exec(html)) !== null) {
38
+ const formBody = formMatch[1] ?? '';
39
+ const inputRegex = /<(?:input|select|textarea)[^>]*name\s*=\s*["'][^"']+["'][^>]*>/gi;
40
+ let inputMatch;
41
+ while ((inputMatch = inputRegex.exec(formBody)) !== null) {
42
+ const tag = inputMatch[0];
43
+ if (/type\s*=\s*["']hidden["']/i.test(tag))
44
+ continue;
45
+ totalFields++;
46
+ if (/aria-description\s*=\s*["'][^"']+["']/i.test(tag) ||
47
+ /aria-describedby\s*=\s*["'][^"']+["']/i.test(tag)) {
48
+ fieldsWithAria++;
49
+ }
50
+ }
51
+ }
52
+ if (totalFields === 0) {
53
+ return createRuleResult('AXE-002', 5, {
54
+ passed: true,
55
+ score: 5,
56
+ message: 'No form fields found in declarative tools',
57
+ });
58
+ }
59
+ const ariaRatio = fieldsWithAria / totalFields;
60
+ if (ariaRatio >= 0.5) {
61
+ return createRuleResult('AXE-002', 5, {
62
+ passed: true,
63
+ score: 5,
64
+ message: `${fieldsWithAria}/${totalFields} fields use ARIA descriptions`,
65
+ });
66
+ }
67
+ const score = Math.round(ariaRatio * 5);
68
+ const fieldsWithout = totalFields - fieldsWithAria;
69
+ return createRuleResult('AXE-002', 5, {
70
+ passed: false,
71
+ score,
72
+ message: `${fieldsWithout}/${totalFields} form fields lack ARIA descriptions`,
73
+ details: [
74
+ `Only ${fieldsWithAria} of ${totalFields} fields have aria-description or aria-describedby`,
75
+ 'ARIA descriptions enhance agent understanding of field purpose',
76
+ ],
77
+ suggestions: [
78
+ 'Add aria-description to form fields for richer parameter descriptions',
79
+ 'Example: <input aria-description="Enter a valid email address">',
80
+ 'ARIA descriptions are used as fallback when toolparamdescription is absent',
81
+ ],
82
+ });
83
+ },
84
+ };
85
+ //# sourceMappingURL=AXE-002.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AXE-002.js","sourceRoot":"","sources":["../../../src/rules/accessibility/AXE-002.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * AXE-003: Select Option Descriptions
3
+ *
4
+ * Checks that <select> options have meaningful text content,
5
+ * not just values, so agents can understand available choices.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const AXE_003: Rule;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * AXE-003: Select Option Descriptions
3
+ *
4
+ * Checks that <select> options have meaningful text content,
5
+ * not just values, so agents can understand available choices.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ export const AXE_003 = {
9
+ id: 'AXE-003',
10
+ category: 'accessibility',
11
+ name: 'Select Option Descriptions',
12
+ description: 'Select options should have meaningful text content for agent understanding',
13
+ severity: 'info',
14
+ maxScore: 5,
15
+ async check(context) {
16
+ const declarativeTools = context.tools.filter((t) => t.source === 'declarative');
17
+ if (declarativeTools.length === 0) {
18
+ return createRuleResult('AXE-003', 5, {
19
+ passed: true,
20
+ score: 5,
21
+ message: 'No declarative tools detected (rule not applicable)',
22
+ });
23
+ }
24
+ const html = context.html ?? '';
25
+ if (html.length === 0) {
26
+ return createRuleResult('AXE-003', 5, {
27
+ passed: true,
28
+ score: 5,
29
+ message: 'No HTML available for analysis',
30
+ });
31
+ }
32
+ // Find select elements inside toolname forms
33
+ const formRegex = /<form[^>]*toolname\s*=\s*["']([^"']+)["'][^>]*>([\s\S]*?)<\/form>/gi;
34
+ const poorSelects = [];
35
+ let formMatch;
36
+ while ((formMatch = formRegex.exec(html)) !== null) {
37
+ const toolname = formMatch[1] ?? '';
38
+ const formBody = formMatch[2] ?? '';
39
+ const selectRegex = /<select[^>]*name\s*=\s*["']([^"']+)["'][^>]*>([\s\S]*?)<\/select>/gi;
40
+ let selectMatch;
41
+ while ((selectMatch = selectRegex.exec(formBody)) !== null) {
42
+ const selectName = selectMatch[1] ?? '';
43
+ const selectBody = selectMatch[2] ?? '';
44
+ // Extract options
45
+ const optionRegex = /<option[^>]*value\s*=\s*["']([^"']*)["'][^>]*>([^<]*)<\/option>/gi;
46
+ let optionMatch;
47
+ let totalOptions = 0;
48
+ let poorOptions = 0;
49
+ while ((optionMatch = optionRegex.exec(selectBody)) !== null) {
50
+ const value = optionMatch[1] ?? '';
51
+ const text = (optionMatch[2] ?? '').trim();
52
+ if (!value && !text)
53
+ continue; // skip empty placeholder options
54
+ totalOptions++;
55
+ // Check if text is just the value repeated or very short
56
+ if (text.length === 0 ||
57
+ text === value ||
58
+ /^\d+$/.test(text)) {
59
+ poorOptions++;
60
+ }
61
+ }
62
+ if (totalOptions > 0 && poorOptions > totalOptions / 2) {
63
+ poorSelects.push({
64
+ tool: toolname,
65
+ select: selectName,
66
+ issue: `${poorOptions}/${totalOptions} options have poor or missing text`,
67
+ });
68
+ }
69
+ }
70
+ }
71
+ if (poorSelects.length === 0) {
72
+ return createRuleResult('AXE-003', 5, {
73
+ passed: true,
74
+ score: 5,
75
+ message: 'All select options have meaningful text content',
76
+ });
77
+ }
78
+ const penalty = Math.min(poorSelects.length * 2, 5);
79
+ const score = Math.max(0, 5 - penalty);
80
+ return createRuleResult('AXE-003', 5, {
81
+ passed: false,
82
+ score,
83
+ message: `${poorSelects.length} select element(s) have poor option descriptions`,
84
+ details: poorSelects.map((s) => `Tool "${s.tool}" select "${s.select}": ${s.issue}`),
85
+ suggestions: [
86
+ 'Ensure <option> elements have descriptive text, not just value attributes',
87
+ 'Use human-readable text: <option value="us">United States</option>',
88
+ 'Agents use option text to understand available choices',
89
+ ],
90
+ affectedTools: [...new Set(poorSelects.map((s) => s.tool))],
91
+ });
92
+ },
93
+ };
94
+ //# sourceMappingURL=AXE-003.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AXE-003.js","sourceRoot":"","sources":["../../../src/rules/accessibility/AXE-003.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * AXE-004: Radio Group Descriptions
3
+ *
4
+ * Checks that radio button groups have a toolparamdescription
5
+ * on the first input, so agents understand what the group represents.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const AXE_004: Rule;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * AXE-004: Radio Group Descriptions
3
+ *
4
+ * Checks that radio button groups have a toolparamdescription
5
+ * on the first input, so agents understand what the group represents.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ export const AXE_004 = {
9
+ id: 'AXE-004',
10
+ category: 'accessibility',
11
+ name: 'Radio Group Descriptions',
12
+ description: 'Radio groups should have toolparamdescription on the first input',
13
+ severity: 'info',
14
+ maxScore: 5,
15
+ async check(context) {
16
+ const declarativeTools = context.tools.filter((t) => t.source === 'declarative');
17
+ if (declarativeTools.length === 0) {
18
+ return createRuleResult('AXE-004', 5, {
19
+ passed: true,
20
+ score: 5,
21
+ message: 'No declarative tools detected (rule not applicable)',
22
+ });
23
+ }
24
+ const html = context.html ?? '';
25
+ if (html.length === 0) {
26
+ return createRuleResult('AXE-004', 5, {
27
+ passed: true,
28
+ score: 5,
29
+ message: 'No HTML available for analysis',
30
+ });
31
+ }
32
+ const formRegex = /<form[^>]*toolname\s*=\s*["']([^"']+)["'][^>]*>([\s\S]*?)<\/form>/gi;
33
+ const violations = [];
34
+ let formMatch;
35
+ while ((formMatch = formRegex.exec(html)) !== null) {
36
+ const toolname = formMatch[1] ?? '';
37
+ const formBody = formMatch[2] ?? '';
38
+ // Find radio inputs grouped by name
39
+ const radioRegex = /<input[^>]*type\s*=\s*["']radio["'][^>]*name\s*=\s*["']([^"']+)["'][^>]*>/gi;
40
+ const radioGroups = new Map();
41
+ let radioMatch;
42
+ while ((radioMatch = radioRegex.exec(formBody)) !== null) {
43
+ const name = radioMatch[1] ?? '';
44
+ const tag = radioMatch[0];
45
+ if (!radioGroups.has(name)) {
46
+ radioGroups.set(name, []);
47
+ }
48
+ radioGroups.get(name).push(tag);
49
+ }
50
+ // Also check reverse attribute order (type after name)
51
+ const radioRegex2 = /<input[^>]*name\s*=\s*["']([^"']+)["'][^>]*type\s*=\s*["']radio["'][^>]*>/gi;
52
+ while ((radioMatch = radioRegex2.exec(formBody)) !== null) {
53
+ const name = radioMatch[1] ?? '';
54
+ const tag = radioMatch[0];
55
+ if (!radioGroups.has(name)) {
56
+ radioGroups.set(name, []);
57
+ }
58
+ const existing = radioGroups.get(name);
59
+ if (!existing.includes(tag)) {
60
+ existing.push(tag);
61
+ }
62
+ }
63
+ for (const [groupName, tags] of radioGroups) {
64
+ if (tags.length < 2)
65
+ continue;
66
+ // Check if the first radio in the group has toolparamdescription
67
+ const firstTag = tags[0] ?? '';
68
+ const hasDesc = /toolparamdescription\s*=\s*["'][^"']+["']/i.test(firstTag);
69
+ // Also check if any radio has it
70
+ const anyHasDesc = tags.some((t) => /toolparamdescription\s*=\s*["'][^"']+["']/i.test(t));
71
+ // Also check for a fieldset/legend or associated label
72
+ const hasFieldset = new RegExp(`<fieldset[^>]*>[\\s\\S]*?${groupName}[\\s\\S]*?<legend[^>]*>([^<]+)</legend>`, 'i').test(formBody);
73
+ if (!hasDesc && !anyHasDesc && !hasFieldset) {
74
+ violations.push({ tool: toolname, group: groupName });
75
+ }
76
+ }
77
+ }
78
+ if (violations.length === 0) {
79
+ return createRuleResult('AXE-004', 5, {
80
+ passed: true,
81
+ score: 5,
82
+ message: 'All radio groups have adequate descriptions',
83
+ });
84
+ }
85
+ const penalty = Math.min(violations.length * 2, 5);
86
+ const score = Math.max(0, 5 - penalty);
87
+ return createRuleResult('AXE-004', 5, {
88
+ passed: false,
89
+ score,
90
+ message: `${violations.length} radio group(s) lack descriptions`,
91
+ details: violations.map((v) => `Tool "${v.tool}" radio group "${v.group}" has no toolparamdescription or fieldset legend`),
92
+ suggestions: [
93
+ 'Add toolparamdescription to the first radio input in each group',
94
+ 'Or wrap radio groups in <fieldset> with a descriptive <legend>',
95
+ 'Example: <input type="radio" name="size" toolparamdescription="Product size selection">',
96
+ ],
97
+ affectedTools: [...new Set(violations.map((v) => v.tool))],
98
+ });
99
+ },
100
+ };
101
+ //# sourceMappingURL=AXE-004.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AXE-004.js","sourceRoot":"","sources":["../../../src/rules/accessibility/AXE-004.ts"],"names":[],"mappings":"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"}