@monoes/monobrowse 1.0.11 → 1.0.13

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 (380) hide show
  1. package/dist/src/__tests__/adapters.test.js +5 -2
  2. package/dist/src/__tests__/adapters.test.js.map +1 -1
  3. package/dist/src/__tests__/batch-eval-parsing.test.js +4 -2
  4. package/dist/src/__tests__/batch-eval-parsing.test.js.map +1 -1
  5. package/dist/src/__tests__/box-model.test.js +9 -5
  6. package/dist/src/__tests__/box-model.test.js.map +1 -1
  7. package/dist/src/__tests__/browser-launch.test.d.ts +10 -0
  8. package/dist/src/__tests__/browser-launch.test.d.ts.map +1 -1
  9. package/dist/src/__tests__/browser-launch.test.js +17 -10
  10. package/dist/src/__tests__/browser-launch.test.js.map +1 -1
  11. package/dist/src/__tests__/cdp-client.test.js +9 -3
  12. package/dist/src/__tests__/cdp-client.test.js.map +1 -1
  13. package/dist/src/__tests__/close-browser.test.d.ts +13 -0
  14. package/dist/src/__tests__/close-browser.test.d.ts.map +1 -1
  15. package/dist/src/__tests__/close-browser.test.js +5 -5
  16. package/dist/src/__tests__/close-browser.test.js.map +1 -1
  17. package/dist/src/__tests__/console-log.test.js +17 -6
  18. package/dist/src/__tests__/console-log.test.js.map +1 -1
  19. package/dist/src/__tests__/find.test.js +26 -6
  20. package/dist/src/__tests__/find.test.js.map +1 -1
  21. package/dist/src/__tests__/frame-command.test.js +9 -3
  22. package/dist/src/__tests__/frame-command.test.js.map +1 -1
  23. package/dist/src/__tests__/open-url-navigate-error.test.js +1 -3
  24. package/dist/src/__tests__/open-url-navigate-error.test.js.map +1 -1
  25. package/dist/src/__tests__/output.test.js +1 -1
  26. package/dist/src/__tests__/output.test.js.map +1 -1
  27. package/dist/src/__tests__/profiler-heap.test.d.ts +2 -0
  28. package/dist/src/__tests__/profiler-heap.test.d.ts.map +1 -0
  29. package/dist/src/__tests__/profiler-heap.test.js +154 -0
  30. package/dist/src/__tests__/profiler-heap.test.js.map +1 -0
  31. package/dist/src/__tests__/reap-idle-browser.test.d.ts +15 -0
  32. package/dist/src/__tests__/reap-idle-browser.test.d.ts.map +1 -0
  33. package/dist/src/__tests__/reap-idle-browser.test.js +238 -0
  34. package/dist/src/__tests__/reap-idle-browser.test.js.map +1 -0
  35. package/dist/src/__tests__/ref-cache.test.js +18 -10
  36. package/dist/src/__tests__/ref-cache.test.js.map +1 -1
  37. package/dist/src/__tests__/report-a11y.test.d.ts +2 -0
  38. package/dist/src/__tests__/report-a11y.test.d.ts.map +1 -0
  39. package/dist/src/__tests__/report-a11y.test.js +147 -0
  40. package/dist/src/__tests__/report-a11y.test.js.map +1 -0
  41. package/dist/src/__tests__/report-analyze.test.d.ts +2 -0
  42. package/dist/src/__tests__/report-analyze.test.d.ts.map +1 -0
  43. package/dist/src/__tests__/report-analyze.test.js +157 -0
  44. package/dist/src/__tests__/report-analyze.test.js.map +1 -0
  45. package/dist/src/__tests__/report-budget.test.d.ts +2 -0
  46. package/dist/src/__tests__/report-budget.test.d.ts.map +1 -0
  47. package/dist/src/__tests__/report-budget.test.js +80 -0
  48. package/dist/src/__tests__/report-budget.test.js.map +1 -0
  49. package/dist/src/__tests__/report-diff.test.d.ts +2 -0
  50. package/dist/src/__tests__/report-diff.test.d.ts.map +1 -0
  51. package/dist/src/__tests__/report-diff.test.js +231 -0
  52. package/dist/src/__tests__/report-diff.test.js.map +1 -0
  53. package/dist/src/__tests__/report-evidence.test.d.ts +2 -0
  54. package/dist/src/__tests__/report-evidence.test.d.ts.map +1 -0
  55. package/dist/src/__tests__/report-evidence.test.js +214 -0
  56. package/dist/src/__tests__/report-evidence.test.js.map +1 -0
  57. package/dist/src/__tests__/report-flake.test.d.ts +2 -0
  58. package/dist/src/__tests__/report-flake.test.d.ts.map +1 -0
  59. package/dist/src/__tests__/report-flake.test.js +182 -0
  60. package/dist/src/__tests__/report-flake.test.js.map +1 -0
  61. package/dist/src/__tests__/report-history.test.d.ts +2 -0
  62. package/dist/src/__tests__/report-history.test.d.ts.map +1 -0
  63. package/dist/src/__tests__/report-history.test.js +229 -0
  64. package/dist/src/__tests__/report-history.test.js.map +1 -0
  65. package/dist/src/__tests__/report-output.test.d.ts +2 -0
  66. package/dist/src/__tests__/report-output.test.d.ts.map +1 -0
  67. package/dist/src/__tests__/report-output.test.js +169 -0
  68. package/dist/src/__tests__/report-output.test.js.map +1 -0
  69. package/dist/src/__tests__/report-png.test.d.ts +2 -0
  70. package/dist/src/__tests__/report-png.test.d.ts.map +1 -0
  71. package/dist/src/__tests__/report-png.test.js +84 -0
  72. package/dist/src/__tests__/report-png.test.js.map +1 -0
  73. package/dist/src/__tests__/report-render-extras.test.d.ts +2 -0
  74. package/dist/src/__tests__/report-render-extras.test.d.ts.map +1 -0
  75. package/dist/src/__tests__/report-render-extras.test.js +285 -0
  76. package/dist/src/__tests__/report-render-extras.test.js.map +1 -0
  77. package/dist/src/__tests__/report-render.test.d.ts +2 -0
  78. package/dist/src/__tests__/report-render.test.d.ts.map +1 -0
  79. package/dist/src/__tests__/report-render.test.js +143 -0
  80. package/dist/src/__tests__/report-render.test.js.map +1 -0
  81. package/dist/src/__tests__/report-run.test.d.ts +2 -0
  82. package/dist/src/__tests__/report-run.test.d.ts.map +1 -0
  83. package/dist/src/__tests__/report-run.test.js +234 -0
  84. package/dist/src/__tests__/report-run.test.js.map +1 -0
  85. package/dist/src/__tests__/session-state.test.d.ts +8 -0
  86. package/dist/src/__tests__/session-state.test.d.ts.map +1 -1
  87. package/dist/src/__tests__/session-state.test.js +10 -10
  88. package/dist/src/__tests__/session-state.test.js.map +1 -1
  89. package/dist/src/__tests__/snapshot.test.js +44 -14
  90. package/dist/src/__tests__/snapshot.test.js.map +1 -1
  91. package/dist/src/__tests__/storage-eval.test.js +14 -6
  92. package/dist/src/__tests__/storage-eval.test.js.map +1 -1
  93. package/dist/src/__tests__/tabs.test.js +25 -9
  94. package/dist/src/__tests__/tabs.test.js.map +1 -1
  95. package/dist/src/browser/action-builder/analyzer.d.ts +1 -1
  96. package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -1
  97. package/dist/src/browser/action-builder/analyzer.js +25 -9
  98. package/dist/src/browser/action-builder/analyzer.js.map +1 -1
  99. package/dist/src/browser/action-builder/types.d.ts.map +1 -1
  100. package/dist/src/browser/actions.d.ts +1 -1
  101. package/dist/src/browser/actions.d.ts.map +1 -1
  102. package/dist/src/browser/actions.js +16 -6
  103. package/dist/src/browser/actions.js.map +1 -1
  104. package/dist/src/browser/adapters/gemini.d.ts.map +1 -1
  105. package/dist/src/browser/adapters/gemini.js.map +1 -1
  106. package/dist/src/browser/adapters/google.d.ts.map +1 -1
  107. package/dist/src/browser/adapters/google.js.map +1 -1
  108. package/dist/src/browser/adapters/index.d.ts.map +1 -1
  109. package/dist/src/browser/adapters/index.js +3 -3
  110. package/dist/src/browser/adapters/index.js.map +1 -1
  111. package/dist/src/browser/adapters/instagram.d.ts.map +1 -1
  112. package/dist/src/browser/adapters/instagram.js.map +1 -1
  113. package/dist/src/browser/adapters/linkedin.d.ts.map +1 -1
  114. package/dist/src/browser/adapters/linkedin.js.map +1 -1
  115. package/dist/src/browser/adapters/microsoft.d.ts.map +1 -1
  116. package/dist/src/browser/adapters/microsoft.js.map +1 -1
  117. package/dist/src/browser/adapters/x.d.ts.map +1 -1
  118. package/dist/src/browser/adapters/x.js.map +1 -1
  119. package/dist/src/browser/browser.d.ts +30 -0
  120. package/dist/src/browser/browser.d.ts.map +1 -1
  121. package/dist/src/browser/browser.js +122 -15
  122. package/dist/src/browser/browser.js.map +1 -1
  123. package/dist/src/browser/cdp.d.ts.map +1 -1
  124. package/dist/src/browser/cdp.js +21 -8
  125. package/dist/src/browser/cdp.js.map +1 -1
  126. package/dist/src/browser/console-log.d.ts.map +1 -1
  127. package/dist/src/browser/console-log.js +2 -2
  128. package/dist/src/browser/console-log.js.map +1 -1
  129. package/dist/src/browser/dashboard/server.d.ts.map +1 -1
  130. package/dist/src/browser/dashboard/server.js +29 -19
  131. package/dist/src/browser/dashboard/server.js.map +1 -1
  132. package/dist/src/browser/dashboard/ui.html +153 -165
  133. package/dist/src/browser/dialog.d.ts.map +1 -1
  134. package/dist/src/browser/dialog.js +3 -1
  135. package/dist/src/browser/dialog.js.map +1 -1
  136. package/dist/src/browser/emulation.d.ts.map +1 -1
  137. package/dist/src/browser/emulation.js +21 -6
  138. package/dist/src/browser/emulation.js.map +1 -1
  139. package/dist/src/browser/find.d.ts +4 -4
  140. package/dist/src/browser/find.d.ts.map +1 -1
  141. package/dist/src/browser/find.js +14 -6
  142. package/dist/src/browser/find.js.map +1 -1
  143. package/dist/src/browser/har.d.ts.map +1 -1
  144. package/dist/src/browser/har.js +14 -7
  145. package/dist/src/browser/har.js.map +1 -1
  146. package/dist/src/browser/index.d.ts +17 -16
  147. package/dist/src/browser/index.d.ts.map +1 -1
  148. package/dist/src/browser/index.js +17 -16
  149. package/dist/src/browser/index.js.map +1 -1
  150. package/dist/src/browser/network.d.ts.map +1 -1
  151. package/dist/src/browser/network.js +14 -4
  152. package/dist/src/browser/network.js.map +1 -1
  153. package/dist/src/browser/pdf.d.ts.map +1 -1
  154. package/dist/src/browser/pdf.js +3 -3
  155. package/dist/src/browser/pdf.js.map +1 -1
  156. package/dist/src/browser/profiler.d.ts.map +1 -1
  157. package/dist/src/browser/profiler.js +22 -20
  158. package/dist/src/browser/profiler.js.map +1 -1
  159. package/dist/src/browser/record.d.ts +1 -1
  160. package/dist/src/browser/record.d.ts.map +1 -1
  161. package/dist/src/browser/record.js +18 -7
  162. package/dist/src/browser/record.js.map +1 -1
  163. package/dist/src/browser/ref-cache.d.ts.map +1 -1
  164. package/dist/src/browser/ref-cache.js +9 -4
  165. package/dist/src/browser/ref-cache.js.map +1 -1
  166. package/dist/src/browser/screenshot.d.ts.map +1 -1
  167. package/dist/src/browser/screenshot.js +11 -7
  168. package/dist/src/browser/screenshot.js.map +1 -1
  169. package/dist/src/browser/session.d.ts +1 -1
  170. package/dist/src/browser/session.d.ts.map +1 -1
  171. package/dist/src/browser/session.js +27 -8
  172. package/dist/src/browser/session.js.map +1 -1
  173. package/dist/src/browser/snapshot.d.ts +1 -1
  174. package/dist/src/browser/snapshot.d.ts.map +1 -1
  175. package/dist/src/browser/snapshot.js +8 -4
  176. package/dist/src/browser/snapshot.js.map +1 -1
  177. package/dist/src/browser/storage.d.ts.map +1 -1
  178. package/dist/src/browser/storage.js.map +1 -1
  179. package/dist/src/browser/tabs.d.ts.map +1 -1
  180. package/dist/src/browser/tabs.js +9 -3
  181. package/dist/src/browser/tabs.js.map +1 -1
  182. package/dist/src/browser/trace.d.ts.map +1 -1
  183. package/dist/src/browser/trace.js +3 -3
  184. package/dist/src/browser/trace.js.map +1 -1
  185. package/dist/src/browser/types.d.ts.map +1 -1
  186. package/dist/src/browser/types.js +23 -4
  187. package/dist/src/browser/types.js.map +1 -1
  188. package/dist/src/browser/vitals.d.ts.map +1 -1
  189. package/dist/src/browser/vitals.js +1 -1
  190. package/dist/src/browser/vitals.js.map +1 -1
  191. package/dist/src/browser/wait.d.ts.map +1 -1
  192. package/dist/src/browser/wait.js +11 -5
  193. package/dist/src/browser/wait.js.map +1 -1
  194. package/dist/src/cli/action.d.ts.map +1 -1
  195. package/dist/src/cli/action.js +14 -11
  196. package/dist/src/cli/action.js.map +1 -1
  197. package/dist/src/cli/commands.d.ts.map +1 -1
  198. package/dist/src/cli/commands.js +707 -159
  199. package/dist/src/cli/commands.js.map +1 -1
  200. package/dist/src/cli/output.js +5 -5
  201. package/dist/src/cli/output.js.map +1 -1
  202. package/dist/src/cli/platform.d.ts.map +1 -1
  203. package/dist/src/cli/platform.js +15 -11
  204. package/dist/src/cli/platform.js.map +1 -1
  205. package/dist/src/cli/types.d.ts +1 -1
  206. package/dist/src/cli/types.d.ts.map +1 -1
  207. package/dist/src/cli.js +21 -9
  208. package/dist/src/cli.js.map +1 -1
  209. package/dist/src/index.d.ts +2 -2
  210. package/dist/src/index.d.ts.map +1 -1
  211. package/dist/src/index.js +2 -2
  212. package/dist/src/index.js.map +1 -1
  213. package/dist/src/report/a11y.d.ts +52 -0
  214. package/dist/src/report/a11y.d.ts.map +1 -0
  215. package/dist/src/report/a11y.js +231 -0
  216. package/dist/src/report/a11y.js.map +1 -0
  217. package/dist/src/report/analyze.d.ts +20 -0
  218. package/dist/src/report/analyze.d.ts.map +1 -0
  219. package/dist/src/report/analyze.js +136 -0
  220. package/dist/src/report/analyze.js.map +1 -0
  221. package/dist/src/report/budget.d.ts +27 -0
  222. package/dist/src/report/budget.d.ts.map +1 -0
  223. package/dist/src/report/budget.js +143 -0
  224. package/dist/src/report/budget.js.map +1 -0
  225. package/dist/src/report/collect-a11y.d.ts +25 -0
  226. package/dist/src/report/collect-a11y.d.ts.map +1 -0
  227. package/dist/src/report/collect-a11y.js +154 -0
  228. package/dist/src/report/collect-a11y.js.map +1 -0
  229. package/dist/src/report/collect.d.ts +45 -0
  230. package/dist/src/report/collect.d.ts.map +1 -0
  231. package/dist/src/report/collect.js +275 -0
  232. package/dist/src/report/collect.js.map +1 -0
  233. package/dist/src/report/evidence.d.ts +80 -0
  234. package/dist/src/report/evidence.d.ts.map +1 -0
  235. package/dist/src/report/evidence.js +279 -0
  236. package/dist/src/report/evidence.js.map +1 -0
  237. package/dist/src/report/flake.d.ts +88 -0
  238. package/dist/src/report/flake.d.ts.map +1 -0
  239. package/dist/src/report/flake.js +232 -0
  240. package/dist/src/report/flake.js.map +1 -0
  241. package/dist/src/report/history.d.ts +85 -0
  242. package/dist/src/report/history.d.ts.map +1 -0
  243. package/dist/src/report/history.js +176 -0
  244. package/dist/src/report/history.js.map +1 -0
  245. package/dist/src/report/index.d.ts +102 -0
  246. package/dist/src/report/index.d.ts.map +1 -0
  247. package/dist/src/report/index.js +216 -0
  248. package/dist/src/report/index.js.map +1 -0
  249. package/dist/src/report/pixel-diff.d.ts +30 -0
  250. package/dist/src/report/pixel-diff.d.ts.map +1 -0
  251. package/dist/src/report/pixel-diff.js +175 -0
  252. package/dist/src/report/pixel-diff.js.map +1 -0
  253. package/dist/src/report/png.d.ts +28 -0
  254. package/dist/src/report/png.d.ts.map +1 -0
  255. package/dist/src/report/png.js +259 -0
  256. package/dist/src/report/png.js.map +1 -0
  257. package/dist/src/report/render-evidence.d.ts +16 -0
  258. package/dist/src/report/render-evidence.d.ts.map +1 -0
  259. package/dist/src/report/render-evidence.js +174 -0
  260. package/dist/src/report/render-evidence.js.map +1 -0
  261. package/dist/src/report/render-trend.d.ts +16 -0
  262. package/dist/src/report/render-trend.d.ts.map +1 -0
  263. package/dist/src/report/render-trend.js +194 -0
  264. package/dist/src/report/render-trend.js.map +1 -0
  265. package/dist/src/report/render.d.ts +21 -0
  266. package/dist/src/report/render.d.ts.map +1 -0
  267. package/dist/src/report/render.js +286 -0
  268. package/dist/src/report/render.js.map +1 -0
  269. package/dist/src/report/run-diff.d.ts +18 -0
  270. package/dist/src/report/run-diff.d.ts.map +1 -0
  271. package/dist/src/report/run-diff.js +51 -0
  272. package/dist/src/report/run-diff.js.map +1 -0
  273. package/dist/src/report/structure.d.ts +41 -0
  274. package/dist/src/report/structure.d.ts.map +1 -0
  275. package/dist/src/report/structure.js +210 -0
  276. package/dist/src/report/structure.js.map +1 -0
  277. package/dist/src/report/trend.d.ts +26 -0
  278. package/dist/src/report/trend.d.ts.map +1 -0
  279. package/dist/src/report/trend.js +164 -0
  280. package/dist/src/report/trend.js.map +1 -0
  281. package/dist/src/report/types.d.ts +320 -0
  282. package/dist/src/report/types.d.ts.map +1 -0
  283. package/dist/src/report/types.js +11 -0
  284. package/dist/src/report/types.js.map +1 -0
  285. package/dist/src/report/util.d.ts +17 -0
  286. package/dist/src/report/util.d.ts.map +1 -0
  287. package/dist/src/report/util.js +45 -0
  288. package/dist/src/report/util.js.map +1 -0
  289. package/dist/tsconfig.tsbuildinfo +1 -1
  290. package/package.json +1 -1
  291. package/src/__tests__/adapters.test.ts +15 -8
  292. package/src/__tests__/batch-eval-parsing.test.ts +4 -2
  293. package/src/__tests__/box-model.test.ts +9 -5
  294. package/src/__tests__/browser-launch.test.ts +26 -15
  295. package/src/__tests__/cdp-client.test.ts +25 -9
  296. package/src/__tests__/close-browser.test.ts +11 -6
  297. package/src/__tests__/console-log.test.ts +36 -13
  298. package/src/__tests__/find.test.ts +58 -20
  299. package/src/__tests__/frame-command.test.ts +12 -6
  300. package/src/__tests__/open-url-navigate-error.test.ts +2 -4
  301. package/src/__tests__/output.test.ts +1 -1
  302. package/src/__tests__/profiler-heap.test.ts +191 -0
  303. package/src/__tests__/reap-idle-browser.test.ts +276 -0
  304. package/src/__tests__/ref-cache.test.ts +25 -12
  305. package/src/__tests__/report-a11y.test.ts +180 -0
  306. package/src/__tests__/report-analyze.test.ts +202 -0
  307. package/src/__tests__/report-budget.test.ts +92 -0
  308. package/src/__tests__/report-diff.test.ts +272 -0
  309. package/src/__tests__/report-evidence.test.ts +276 -0
  310. package/src/__tests__/report-flake.test.ts +214 -0
  311. package/src/__tests__/report-history.test.ts +293 -0
  312. package/src/__tests__/report-output.test.ts +210 -0
  313. package/src/__tests__/report-png.test.ts +95 -0
  314. package/src/__tests__/report-render-extras.test.ts +315 -0
  315. package/src/__tests__/report-render.test.ts +175 -0
  316. package/src/__tests__/report-run.test.ts +264 -0
  317. package/src/__tests__/session-state.test.ts +40 -28
  318. package/src/__tests__/snapshot.test.ts +60 -21
  319. package/src/__tests__/storage-eval.test.ts +30 -16
  320. package/src/__tests__/tabs.test.ts +26 -10
  321. package/src/browser/action-builder/analyzer.ts +39 -15
  322. package/src/browser/action-builder/types.ts +45 -8
  323. package/src/browser/actions.ts +288 -123
  324. package/src/browser/adapters/gemini.ts +7 -3
  325. package/src/browser/adapters/google.ts +5 -3
  326. package/src/browser/adapters/index.ts +5 -4
  327. package/src/browser/adapters/instagram.ts +4 -2
  328. package/src/browser/adapters/linkedin.ts +4 -2
  329. package/src/browser/adapters/microsoft.ts +9 -5
  330. package/src/browser/adapters/x.ts +7 -3
  331. package/src/browser/browser.ts +188 -42
  332. package/src/browser/cdp.ts +43 -11
  333. package/src/browser/console-log.ts +24 -7
  334. package/src/browser/dashboard/server.ts +44 -25
  335. package/src/browser/dashboard/ui.html +153 -165
  336. package/src/browser/dialog.ts +25 -8
  337. package/src/browser/emulation.ts +86 -36
  338. package/src/browser/find.ts +110 -47
  339. package/src/browser/har.ts +58 -26
  340. package/src/browser/index.ts +17 -16
  341. package/src/browser/network.ts +147 -46
  342. package/src/browser/pdf.ts +4 -4
  343. package/src/browser/profiler.ts +39 -19
  344. package/src/browser/record.ts +38 -19
  345. package/src/browser/ref-cache.ts +38 -17
  346. package/src/browser/screenshot.ts +58 -32
  347. package/src/browser/session.ts +51 -18
  348. package/src/browser/snapshot.ts +34 -20
  349. package/src/browser/storage.ts +73 -17
  350. package/src/browser/tabs.ts +29 -17
  351. package/src/browser/trace.ts +14 -10
  352. package/src/browser/types.ts +23 -4
  353. package/src/browser/vitals.ts +33 -16
  354. package/src/browser/wait.ts +47 -21
  355. package/src/cli/action.ts +52 -21
  356. package/src/cli/commands.ts +1365 -385
  357. package/src/cli/output.ts +5 -5
  358. package/src/cli/platform.ts +44 -23
  359. package/src/cli/types.ts +1 -1
  360. package/src/cli.ts +34 -18
  361. package/src/index.ts +2 -2
  362. package/src/report/a11y.ts +242 -0
  363. package/src/report/analyze.ts +181 -0
  364. package/src/report/budget.ts +155 -0
  365. package/src/report/collect-a11y.ts +200 -0
  366. package/src/report/collect.ts +408 -0
  367. package/src/report/evidence.ts +379 -0
  368. package/src/report/flake.ts +303 -0
  369. package/src/report/history.ts +219 -0
  370. package/src/report/index.ts +307 -0
  371. package/src/report/pixel-diff.ts +197 -0
  372. package/src/report/png.ts +290 -0
  373. package/src/report/render-evidence.ts +194 -0
  374. package/src/report/render-trend.ts +225 -0
  375. package/src/report/render.ts +314 -0
  376. package/src/report/run-diff.ts +63 -0
  377. package/src/report/structure.ts +212 -0
  378. package/src/report/trend.ts +189 -0
  379. package/src/report/types.ts +356 -0
  380. package/src/report/util.ts +48 -0
package/src/cli/output.ts CHANGED
@@ -17,20 +17,20 @@ class OutputFormatter {
17
17
  }
18
18
 
19
19
  printSuccess(message: string): void {
20
- console.log(this.c('green', '✓') + ' ' + message);
20
+ console.log(`${this.c('green', '✓')} ${message}`);
21
21
  }
22
22
 
23
23
  printError(message: string, details?: string): void {
24
- console.error(this.c('red', '✗') + ' ' + message);
25
- if (details) console.error(this.c('red', ' ' + details));
24
+ console.error(`${this.c('red', '✗')} ${message}`);
25
+ if (details) console.error(this.c('red', ` ${details}`));
26
26
  }
27
27
 
28
28
  printWarning(message: string): void {
29
- console.warn(this.c('yellow', '⚠') + ' ' + message);
29
+ console.warn(`${this.c('yellow', '⚠')} ${message}`);
30
30
  }
31
31
 
32
32
  printInfo(message: string): void {
33
- console.log(this.c('cyan', 'ℹ') + ' ' + message);
33
+ console.log(`${this.c('cyan', 'ℹ')} ${message}`);
34
34
  }
35
35
  }
36
36
 
@@ -3,22 +3,22 @@
3
3
  * Sessions stored as JSON at ~/.monomind/sessions.json
4
4
  */
5
5
 
6
- import { Command } from 'commander';
7
- import { readFile, writeFile, mkdir, chmod } from 'node:fs/promises';
8
6
  import { existsSync } from 'node:fs';
9
- import { join } from 'node:path';
7
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
10
8
  import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { Command } from 'commander';
11
11
  import { getAdapter } from '../index.js';
12
12
 
13
- const CDP_PORT = Number(process.env['MONOMIND_CDP_PORT'] ?? 9222);
13
+ const CDP_PORT = Number(process.env.MONOMIND_CDP_PORT ?? 9222);
14
14
 
15
15
  const SESSIONS_FILE = join(homedir(), '.monomind', 'sessions.json');
16
16
 
17
17
  interface Session {
18
- id: string; // "platform:username"
18
+ id: string; // "platform:username"
19
19
  platform: string;
20
20
  username: string;
21
- cookies: string; // JSON-serialized
21
+ cookies: string; // JSON-serialized
22
22
  userAgent: string;
23
23
  createdAt: number;
24
24
  lastUsedAt: number;
@@ -46,7 +46,9 @@ export function createPlatformCommand(): Command {
46
46
 
47
47
  cmd
48
48
  .command('connect <platform>')
49
- .description('Connect a platform account via browser — opens Chrome, waits for login, saves session')
49
+ .description(
50
+ 'Connect a platform account via browser — opens Chrome, waits for login, saves session',
51
+ )
50
52
  .option('--port <number>', 'CDP port of running Chrome instance', String(CDP_PORT))
51
53
  .option('--timeout <seconds>', 'Login timeout in seconds', '300')
52
54
  .action(async (platform: string, opts: { port: string; timeout: string }) => {
@@ -57,7 +59,9 @@ export function createPlatformCommand(): Command {
57
59
  }
58
60
 
59
61
  let adapter: ReturnType<typeof getAdapter>;
60
- try { adapter = getAdapter(platform); } catch (err) {
62
+ try {
63
+ adapter = getAdapter(platform);
64
+ } catch (err) {
61
65
  console.error(err instanceof Error ? err.message : String(err));
62
66
  process.exit(1);
63
67
  }
@@ -67,7 +71,9 @@ export function createPlatformCommand(): Command {
67
71
 
68
72
  // Lazy-load browser module
69
73
  let browser: typeof import('@monoes/monobrowse');
70
- try { browser = await import('@monoes/monobrowse'); } catch (err) {
74
+ try {
75
+ browser = await import('@monoes/monobrowse');
76
+ } catch (err) {
71
77
  console.error('Browser module unavailable:', err);
72
78
  process.exit(1);
73
79
  }
@@ -91,16 +97,18 @@ export function createPlatformCommand(): Command {
91
97
  const { client, sessionId: sid } = conn;
92
98
 
93
99
  try {
94
- const refs = new Map();
100
+ const _refs = new Map();
95
101
 
96
102
  // Restore any existing cookies first so the user might already be logged in
97
103
  const sessions = await loadSessions();
98
- const existingSession = sessions.find(s => s.platform === platform);
104
+ const existingSession = sessions.find((s) => s.platform === platform);
99
105
  if (existingSession?.cookies) {
100
106
  try {
101
107
  const cookies = JSON.parse(existingSession.cookies);
102
108
  if (cookies.length > 0) await browser.setCookies(client, sid, cookies);
103
- } catch { /* ignore */ }
109
+ } catch {
110
+ /* ignore */
111
+ }
104
112
  }
105
113
 
106
114
  // Navigate to login URL
@@ -120,25 +128,31 @@ export function createPlatformCommand(): Command {
120
128
  console.log(`│ Please sign in to ${platform.padEnd(34)}│`);
121
129
  console.log('│ │');
122
130
  console.log('│ Complete the login in the browser window. │');
123
- console.log(`│ Waiting up to ${Math.round(timeoutMs / 60000)} minutes for login… │`);
131
+ console.log(
132
+ `│ Waiting up to ${Math.round(timeoutMs / 60000)} minutes for login… │`,
133
+ );
124
134
  console.log('└──────────────────────────────────────────────────────┘');
125
135
  console.log('');
126
136
 
127
137
  const deadline = Date.now() + timeoutMs;
128
138
  while (Date.now() < deadline) {
129
- await new Promise(r => setTimeout(r, 2000));
139
+ await new Promise((r) => setTimeout(r, 2000));
130
140
  isLoggedIn = await adapter.isLoggedIn(pageIface).catch(() => false);
131
141
  if (isLoggedIn) break;
132
142
  // Navigate to app URL if user manually browsed away from login
133
143
  const currentUrl = await browser.getCurrentUrl(client, sid).catch(() => '');
134
- if (!currentUrl.includes(new URL(adapter.loginURL()).hostname) &&
135
- !currentUrl.includes(new URL(adapter.baseURL).hostname)) {
144
+ if (
145
+ !currentUrl.includes(new URL(adapter.loginURL()).hostname) &&
146
+ !currentUrl.includes(new URL(adapter.baseURL).hostname)
147
+ ) {
136
148
  await browser.openUrl(client, sid, adapter.loginURL());
137
149
  }
138
150
  }
139
151
 
140
152
  if (!isLoggedIn) {
141
- console.error(`\nLogin timeout after ${Math.round(timeoutMs / 1000)}s. Session not saved.`);
153
+ console.error(
154
+ `\nLogin timeout after ${Math.round(timeoutMs / 1000)}s. Session not saved.`,
155
+ );
142
156
  process.exit(1);
143
157
  }
144
158
  }
@@ -147,7 +161,9 @@ export function createPlatformCommand(): Command {
147
161
 
148
162
  // Capture cookies and user agent
149
163
  const liveCookies = await browser.getCookies(client, sid).catch(() => []);
150
- const userAgent = await browser.evaluateJs(client, sid, 'navigator.userAgent').catch(() => 'unknown') as string;
164
+ const userAgent = (await browser
165
+ .evaluateJs(client, sid, 'navigator.userAgent')
166
+ .catch(() => 'unknown')) as string;
151
167
  const username = await adapter.extractUsername(pageIface).catch(() => `${platform}-user`);
152
168
 
153
169
  // Save to sessions file
@@ -160,8 +176,9 @@ export function createPlatformCommand(): Command {
160
176
  createdAt: existingSession?.createdAt ?? Date.now(),
161
177
  lastUsedAt: Date.now(),
162
178
  };
163
- const idx = sessions.findIndex(s => s.platform === platform);
164
- if (idx >= 0) sessions[idx] = sessionEntry; else sessions.push(sessionEntry);
179
+ const idx = sessions.findIndex((s) => s.platform === platform);
180
+ if (idx >= 0) sessions[idx] = sessionEntry;
181
+ else sessions.push(sessionEntry);
165
182
  await saveSessions(sessions);
166
183
 
167
184
  console.log(`\n✓ Session saved: ${sessionEntry.id}`);
@@ -183,11 +200,15 @@ export function createPlatformCommand(): Command {
183
200
  console.log('No connected accounts. Run: browse platform connect <platform>');
184
201
  return;
185
202
  }
186
- console.log(`\n${'ID'.padEnd(40)} ${'PLATFORM'.padEnd(12)} ${'USERNAME'.padEnd(30)} LAST USED`);
203
+ console.log(
204
+ `\n${'ID'.padEnd(40)} ${'PLATFORM'.padEnd(12)} ${'USERNAME'.padEnd(30)} LAST USED`,
205
+ );
187
206
  console.log('─'.repeat(95));
188
207
  for (const s of sessions) {
189
208
  const lastUsed = new Date(s.lastUsedAt).toLocaleString();
190
- console.log(`${s.id.padEnd(40)} ${s.platform.padEnd(12)} ${s.username.padEnd(30)} ${lastUsed}`);
209
+ console.log(
210
+ `${s.id.padEnd(40)} ${s.platform.padEnd(12)} ${s.username.padEnd(30)} ${lastUsed}`,
211
+ );
191
212
  }
192
213
  });
193
214
 
@@ -196,7 +217,7 @@ export function createPlatformCommand(): Command {
196
217
  .description('Remove a saved platform session')
197
218
  .action(async (id: string) => {
198
219
  const sessions = await loadSessions();
199
- const idx = sessions.findIndex(s => s.id === id);
220
+ const idx = sessions.findIndex((s) => s.id === id);
200
221
  if (idx < 0) {
201
222
  console.error(`Session not found: ${id}`);
202
223
  console.log('Run "browse platform list" to see available sessions.');
package/src/cli/types.ts CHANGED
@@ -37,7 +37,7 @@ export interface CommandExample {
37
37
  description: string;
38
38
  }
39
39
 
40
- export type CommandAction = (ctx: CommandContext) => Promise<CommandResult | void>;
40
+ export type CommandAction = (ctx: CommandContext) => Promise<CommandResult | undefined>;
41
41
 
42
42
  export interface CommandResult {
43
43
  success: boolean;
package/src/cli.ts CHANGED
@@ -4,10 +4,10 @@
4
4
  * Dispatches to the same command tree used by `monomind browse`
5
5
  */
6
6
 
7
- import { createRequire } from 'module';
7
+ import { createRequire } from 'node:module';
8
8
  import browseCommand from './cli/commands.js';
9
9
  import { output } from './cli/output.js';
10
- import type { Command, CommandContext, CommandOption, ParsedFlags } from './cli/types.js';
10
+ import type { Command, CommandContext, ParsedFlags } from './cli/types.js';
11
11
 
12
12
  const _require = createRequire(import.meta.url);
13
13
 
@@ -16,7 +16,7 @@ const _require = createRequire(import.meta.url);
16
16
  // ---------------------------------------------------------------------------
17
17
 
18
18
  function findSub(name: string, subs: Command[]): Command | undefined {
19
- return subs.find(s => s.name === name || s.aliases?.includes(name));
19
+ return subs.find((s) => s.name === name || s.aliases?.includes(name));
20
20
  }
21
21
 
22
22
  // ---------------------------------------------------------------------------
@@ -33,11 +33,14 @@ function buildShortMap(cmd: Command | undefined): Record<string, string> {
33
33
  }
34
34
 
35
35
  function isBooleanOpt(name: string, cmd: Command | undefined): boolean {
36
- const opt = cmd?.options?.find(o => o.name === name);
36
+ const opt = cmd?.options?.find((o) => o.name === name);
37
37
  return opt?.type === 'boolean';
38
38
  }
39
39
 
40
- function parseArgv(argv: string[], sub: Command | undefined): { args: string[]; flags: ParsedFlags } {
40
+ function parseArgv(
41
+ argv: string[],
42
+ sub: Command | undefined,
43
+ ): { args: string[]; flags: ParsedFlags } {
41
44
  const shortMap = buildShortMap(sub);
42
45
  const flags: ParsedFlags = { _: [] };
43
46
  const positional: string[] = [];
@@ -81,7 +84,7 @@ function parseArgv(argv: string[], sub: Command | undefined): { args: string[];
81
84
  }
82
85
 
83
86
  // Coerce number options
84
- for (const opt of (sub?.options ?? [])) {
87
+ for (const opt of sub?.options ?? []) {
85
88
  if (opt.type === 'number' && typeof flags[opt.name] === 'string') {
86
89
  flags[opt.name] = Number(flags[opt.name]);
87
90
  }
@@ -95,11 +98,12 @@ function parseArgv(argv: string[], sub: Command | undefined): { args: string[];
95
98
  // ---------------------------------------------------------------------------
96
99
 
97
100
  function printHelp(cmd: Command, prefix = 'monobrowse'): void {
98
- const usage = cmd.name === 'browse'
99
- ? `${prefix} <subcommand> [options]`
100
- : `${prefix} ${cmd.name} [subcommand] [options]`;
101
+ const usage =
102
+ cmd.name === 'browse'
103
+ ? `${prefix} <subcommand> [options]`
104
+ : `${prefix} ${cmd.name} [subcommand] [options]`;
101
105
  console.log(`\nUsage: ${usage}\n`);
102
- console.log(cmd.description + '\n');
106
+ console.log(`${cmd.description}\n`);
103
107
 
104
108
  if (cmd.subcommands?.length) {
105
109
  console.log('Subcommands:');
@@ -160,7 +164,12 @@ async function dispatch(cmd: Command, argv: string[]): Promise<void> {
160
164
  const nested = findSub(rest[0], sub.subcommands);
161
165
  if (nested?.action) {
162
166
  const { args, flags } = parseArgv(rest.slice(1), nested);
163
- const ctx: CommandContext = { args, flags, cwd: process.cwd(), interactive: !!process.stdout.isTTY };
167
+ const ctx: CommandContext = {
168
+ args,
169
+ flags,
170
+ cwd: process.cwd(),
171
+ interactive: !!process.stdout.isTTY,
172
+ };
164
173
  const result = await nested.action(ctx);
165
174
  if (result && !result.success) process.exitCode = result.exitCode ?? 1;
166
175
  return;
@@ -174,7 +183,12 @@ async function dispatch(cmd: Command, argv: string[]): Promise<void> {
174
183
  }
175
184
 
176
185
  const { args, flags } = parseArgv(rest, sub);
177
- const ctx: CommandContext = { args, flags, cwd: process.cwd(), interactive: !!process.stdout.isTTY };
186
+ const ctx: CommandContext = {
187
+ args,
188
+ flags,
189
+ cwd: process.cwd(),
190
+ interactive: !!process.stdout.isTTY,
191
+ };
178
192
  const result = await sub.action(ctx);
179
193
  if (result && !result.success) process.exitCode = result.exitCode ?? 1;
180
194
  }
@@ -200,9 +214,11 @@ async function main(): Promise<void> {
200
214
  await dispatch(browseCommand, userArgs);
201
215
  }
202
216
 
203
- main().then(() => {
204
- process.exit(process.exitCode ?? 0);
205
- }).catch(err => {
206
- output.printError(err instanceof Error ? err.message : String(err));
207
- process.exit(1);
208
- });
217
+ main()
218
+ .then(() => {
219
+ process.exit(process.exitCode ?? 0);
220
+ })
221
+ .catch((err) => {
222
+ output.printError(err instanceof Error ? err.message : String(err));
223
+ process.exit(1);
224
+ });
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- export * from './browser/index.js';
2
1
  export * from './browser/action-builder/analyzer.js';
3
2
  export * from './browser/action-builder/types.js';
4
3
  export * from './browser/adapters/index.js';
5
- export { startDashboard, getDashboard } from './browser/dashboard/server.js';
4
+ export { getDashboard, startDashboard } from './browser/dashboard/server.js';
5
+ export * from './browser/index.js';
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Accessibility rules computed from the CDP accessibility tree.
3
+ *
4
+ * Deliberate scope limit: colour contrast is NOT computed here. Contrast
5
+ * needs resolved foreground/background pixels or computed styles, and the AX
6
+ * tree carries neither — guessing it would produce confident nonsense. The
7
+ * report says so explicitly instead.
8
+ *
9
+ * Every rule is a pure function of a node array, so the fixtures in the tests
10
+ * are just hand-written AX trees.
11
+ */
12
+
13
+ import type { A11yFinding, AxNode, FocusCandidate } from './types.js';
14
+
15
+ /** Roles whose whole purpose is to be activated — a nameless one is unusable. */
16
+ const COMMAND_ROLES = new Set([
17
+ 'button',
18
+ 'link',
19
+ 'menuitem',
20
+ 'menuitemcheckbox',
21
+ 'menuitemradio',
22
+ 'tab',
23
+ 'switch',
24
+ 'treeitem',
25
+ ]);
26
+
27
+ /** Roles that take user input and therefore need a programmatic label. */
28
+ const FORM_FIELD_ROLES = new Set([
29
+ 'textbox',
30
+ 'searchbox',
31
+ 'combobox',
32
+ 'listbox',
33
+ 'checkbox',
34
+ 'radio',
35
+ 'slider',
36
+ 'spinbutton',
37
+ ]);
38
+
39
+ const IMAGE_ROLES = new Set(['image', 'img']);
40
+
41
+ /** Natively focusable tags — a negative tabindex on these removes them from the tab order. */
42
+ const NATIVELY_FOCUSABLE = new Set(['a', 'button', 'input', 'select', 'textarea', 'summary']);
43
+
44
+ function strValue(v: unknown): string {
45
+ return typeof v === 'string' ? v : v == null ? '' : String(v);
46
+ }
47
+
48
+ function roleOf(node: AxNode): string {
49
+ return strValue(node.role?.value).toLowerCase();
50
+ }
51
+
52
+ function nameOf(node: AxNode): string {
53
+ return strValue(node.name?.value).trim();
54
+ }
55
+
56
+ function propOf(node: AxNode, name: string): unknown {
57
+ return node.properties?.find((p) => p.name === name)?.value?.value;
58
+ }
59
+
60
+ function locatorFor(node: AxNode, locators?: Map<number, string>): string {
61
+ if (node.backendDOMNodeId !== undefined) {
62
+ const found = locators?.get(node.backendDOMNodeId);
63
+ if (found) return found;
64
+ }
65
+ return `ax-node:${node.nodeId}`;
66
+ }
67
+
68
+ /**
69
+ * Depth-first document order. `Accessibility.getFullAXTree` usually returns
70
+ * nodes pre-ordered already, but heading-order is the one rule where a
71
+ * reordered array would silently produce wrong findings, so we walk childIds
72
+ * ourselves. Nodes unreachable from a root are appended in array order rather
73
+ * than dropped.
74
+ */
75
+ export function orderNodes(nodes: AxNode[]): AxNode[] {
76
+ const byId = new Map<string, AxNode>();
77
+ for (const n of nodes) byId.set(String(n.nodeId), n);
78
+
79
+ const ids = new Set(byId.keys());
80
+ const roots = nodes.filter((n) => n.parentId === undefined || !ids.has(String(n.parentId)));
81
+
82
+ const seen = new Set<string>();
83
+ const ordered: AxNode[] = [];
84
+ const visit = (node: AxNode): void => {
85
+ const id = String(node.nodeId);
86
+ if (seen.has(id)) return;
87
+ seen.add(id);
88
+ ordered.push(node);
89
+ for (const childId of node.childIds ?? []) {
90
+ const child = byId.get(String(childId));
91
+ if (child) visit(child);
92
+ }
93
+ };
94
+ for (const root of roots) visit(root);
95
+ for (const node of nodes) if (!seen.has(String(node.nodeId))) ordered.push(node);
96
+ return ordered;
97
+ }
98
+
99
+ /**
100
+ * Nameless controls, images without alt text, and unlabelled form fields.
101
+ *
102
+ * `ignored` nodes are skipped: that is how `<img alt="">` correctly escapes
103
+ * the image rule — a deliberately decorative image is dropped from the AX
104
+ * tree, while `<img>` with no alt attribute at all stays in it with an empty
105
+ * name, which is the bug we want to report.
106
+ */
107
+ export function findNamingIssues(nodes: AxNode[], locators?: Map<number, string>): A11yFinding[] {
108
+ const findings: A11yFinding[] = [];
109
+ for (const node of nodes) {
110
+ if (node.ignored) continue;
111
+ const role = roleOf(node);
112
+ if (!role) continue;
113
+ const name = nameOf(node);
114
+ if (name) continue;
115
+
116
+ if (FORM_FIELD_ROLES.has(role)) {
117
+ findings.push({
118
+ rule: 'form-field-no-label',
119
+ impact: 'error',
120
+ role,
121
+ name: null,
122
+ locator: locatorFor(node, locators),
123
+ detail: `${role} has no accessible name — no <label>, aria-label or aria-labelledby resolves to one`,
124
+ });
125
+ continue;
126
+ }
127
+
128
+ if (COMMAND_ROLES.has(role)) {
129
+ findings.push({
130
+ rule: 'unlabelled-control',
131
+ impact: 'error',
132
+ role,
133
+ name: null,
134
+ locator: locatorFor(node, locators),
135
+ detail: `${role} has no accessible name — a screen reader announces it as just "${role}"`,
136
+ });
137
+ continue;
138
+ }
139
+
140
+ if (IMAGE_ROLES.has(role)) {
141
+ findings.push({
142
+ rule: 'image-missing-alt',
143
+ impact: 'error',
144
+ role,
145
+ name: null,
146
+ locator: locatorFor(node, locators),
147
+ detail: 'image has no alt text (use alt="" if it is purely decorative)',
148
+ });
149
+ }
150
+ }
151
+ return findings;
152
+ }
153
+
154
+ /**
155
+ * Headings that skip a level (h1 -> h3), plus a document whose first heading
156
+ * is not h1. Both break the outline screen-reader users navigate by.
157
+ */
158
+ export function findHeadingIssues(nodes: AxNode[], locators?: Map<number, string>): A11yFinding[] {
159
+ const findings: A11yFinding[] = [];
160
+ let previous: number | null = null;
161
+
162
+ for (const node of orderNodes(nodes)) {
163
+ if (node.ignored) continue;
164
+ if (roleOf(node) !== 'heading') continue;
165
+ const raw = propOf(node, 'level');
166
+ const level = typeof raw === 'number' ? raw : Number.parseInt(strValue(raw), 10);
167
+ if (!Number.isFinite(level) || level < 1) continue;
168
+
169
+ const name = nameOf(node);
170
+ if (previous === null) {
171
+ if (level > 1) {
172
+ findings.push({
173
+ rule: 'heading-order-jump',
174
+ impact: 'warning',
175
+ role: 'heading',
176
+ name: name || null,
177
+ locator: locatorFor(node, locators),
178
+ detail: `document outline starts at h${level} — the first heading should be h1`,
179
+ });
180
+ }
181
+ } else if (level > previous + 1) {
182
+ findings.push({
183
+ rule: 'heading-order-jump',
184
+ impact: 'warning',
185
+ role: 'heading',
186
+ name: name || null,
187
+ locator: locatorFor(node, locators),
188
+ detail: `heading level jumps h${previous} -> h${level}`,
189
+ });
190
+ }
191
+ previous = level;
192
+ }
193
+ return findings;
194
+ }
195
+
196
+ /**
197
+ * Interactive elements pulled out of the keyboard tab order by a negative
198
+ * tabindex. They stay clickable with a mouse and stay in the AX tree, so
199
+ * nothing else in this file can see them — the candidates come from a DOM
200
+ * sweep in collect.ts.
201
+ */
202
+ export function findFocusOrderIssues(candidates: FocusCandidate[]): A11yFinding[] {
203
+ const findings: A11yFinding[] = [];
204
+ for (const c of candidates) {
205
+ if (c.tabindex >= 0) continue;
206
+ const interactive = NATIVELY_FOCUSABLE.has(c.tag.toLowerCase()) || c.role !== '';
207
+ if (!interactive) continue;
208
+ findings.push({
209
+ rule: 'negative-tabindex',
210
+ impact: 'warning',
211
+ role: c.role || c.tag.toLowerCase(),
212
+ name: c.name,
213
+ locator: c.locator,
214
+ detail: `<${c.tag.toLowerCase()}> has tabindex="${c.tabindex}" — mouse users can reach it, keyboard users cannot`,
215
+ });
216
+ }
217
+ return findings;
218
+ }
219
+
220
+ export interface A11yInput {
221
+ nodes: AxNode[];
222
+ focusCandidates?: FocusCandidate[];
223
+ locators?: Map<number, string>;
224
+ }
225
+
226
+ export function runA11yRules({ nodes, focusCandidates = [], locators }: A11yInput): A11yFinding[] {
227
+ return [
228
+ ...findNamingIssues(nodes, locators),
229
+ ...findHeadingIssues(nodes, locators),
230
+ ...findFocusOrderIssues(focusCandidates),
231
+ ];
232
+ }
233
+
234
+ export function countA11y(findings: A11yFinding[]): { errors: number; warnings: number } {
235
+ let errors = 0;
236
+ let warnings = 0;
237
+ for (const f of findings) {
238
+ if (f.impact === 'error') errors++;
239
+ else warnings++;
240
+ }
241
+ return { errors, warnings };
242
+ }