uxinspect 0.2.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (493) hide show
  1. package/README.md +332 -22
  2. package/dist/a11y-filter.d.ts +15 -0
  3. package/dist/a11y-filter.d.ts.map +1 -0
  4. package/dist/a11y-filter.js +107 -0
  5. package/dist/a11y-filter.js.map +1 -0
  6. package/dist/ab-compare.d.ts +23 -0
  7. package/dist/ab-compare.d.ts.map +1 -0
  8. package/dist/ab-compare.js +340 -0
  9. package/dist/ab-compare.js.map +1 -0
  10. package/dist/ai-codegen.d.ts +30 -0
  11. package/dist/ai-codegen.d.ts.map +1 -0
  12. package/dist/ai-codegen.js +296 -0
  13. package/dist/ai-codegen.js.map +1 -0
  14. package/dist/ai-triage.d.ts +26 -0
  15. package/dist/ai-triage.d.ts.map +1 -0
  16. package/dist/ai-triage.js +207 -0
  17. package/dist/ai-triage.js.map +1 -0
  18. package/dist/amp.d.ts +32 -0
  19. package/dist/amp.d.ts.map +1 -0
  20. package/dist/amp.js +179 -0
  21. package/dist/amp.js.map +1 -0
  22. package/dist/animation-audit.d.ts +25 -0
  23. package/dist/animation-audit.d.ts.map +1 -0
  24. package/dist/animation-audit.js +296 -0
  25. package/dist/animation-audit.js.map +1 -0
  26. package/dist/api.d.ts +3 -0
  27. package/dist/api.d.ts.map +1 -0
  28. package/dist/api.js +85 -0
  29. package/dist/api.js.map +1 -0
  30. package/dist/aria-audit.d.ts +20 -0
  31. package/dist/aria-audit.d.ts.map +1 -0
  32. package/dist/aria-audit.js +445 -0
  33. package/dist/aria-audit.js.map +1 -0
  34. package/dist/assertions.d.ts +30 -0
  35. package/dist/assertions.d.ts.map +1 -0
  36. package/dist/assertions.js +342 -0
  37. package/dist/assertions.js.map +1 -0
  38. package/dist/autofix.d.ts +40 -0
  39. package/dist/autofix.d.ts.map +1 -0
  40. package/dist/autofix.js +244 -0
  41. package/dist/autofix.js.map +1 -0
  42. package/dist/badge.d.ts +27 -0
  43. package/dist/badge.d.ts.map +1 -0
  44. package/dist/badge.js +183 -0
  45. package/dist/badge.js.map +1 -0
  46. package/dist/baseline-drift.d.ts +43 -0
  47. package/dist/baseline-drift.d.ts.map +1 -0
  48. package/dist/baseline-drift.js +208 -0
  49. package/dist/baseline-drift.js.map +1 -0
  50. package/dist/bdd.d.ts +31 -0
  51. package/dist/bdd.d.ts.map +1 -0
  52. package/dist/bdd.js +316 -0
  53. package/dist/bdd.js.map +1 -0
  54. package/dist/bisect.d.ts +32 -0
  55. package/dist/bisect.d.ts.map +1 -0
  56. package/dist/bisect.js +253 -0
  57. package/dist/bisect.js.map +1 -0
  58. package/dist/budget-diff.d.ts +37 -0
  59. package/dist/budget-diff.d.ts.map +1 -0
  60. package/dist/budget-diff.js +273 -0
  61. package/dist/budget-diff.js.map +1 -0
  62. package/dist/budget-file.d.ts +15 -0
  63. package/dist/budget-file.d.ts.map +1 -0
  64. package/dist/budget-file.js +185 -0
  65. package/dist/budget-file.js.map +1 -0
  66. package/dist/bundle-size.d.ts +36 -0
  67. package/dist/bundle-size.d.ts.map +1 -0
  68. package/dist/bundle-size.js +347 -0
  69. package/dist/bundle-size.js.map +1 -0
  70. package/dist/cache-headers.d.ts +33 -0
  71. package/dist/cache-headers.d.ts.map +1 -0
  72. package/dist/cache-headers.js +270 -0
  73. package/dist/cache-headers.js.map +1 -0
  74. package/dist/canonical-audit.d.ts +19 -0
  75. package/dist/canonical-audit.d.ts.map +1 -0
  76. package/dist/canonical-audit.js +196 -0
  77. package/dist/canonical-audit.js.map +1 -0
  78. package/dist/chaos.d.ts +38 -0
  79. package/dist/chaos.d.ts.map +1 -0
  80. package/dist/chaos.js +348 -0
  81. package/dist/chaos.js.map +1 -0
  82. package/dist/cli.js +201 -23
  83. package/dist/cli.js.map +1 -1
  84. package/dist/clickjacking-audit.d.ts +18 -0
  85. package/dist/clickjacking-audit.d.ts.map +1 -0
  86. package/dist/clickjacking-audit.js +231 -0
  87. package/dist/clickjacking-audit.js.map +1 -0
  88. package/dist/cls-culprit.d.ts +36 -0
  89. package/dist/cls-culprit.d.ts.map +1 -0
  90. package/dist/cls-culprit.js +203 -0
  91. package/dist/cls-culprit.js.map +1 -0
  92. package/dist/cls-timeline.d.ts +30 -0
  93. package/dist/cls-timeline.d.ts.map +1 -0
  94. package/dist/cls-timeline.js +61 -0
  95. package/dist/cls-timeline.js.map +1 -0
  96. package/dist/codegen-converter.d.ts +19 -0
  97. package/dist/codegen-converter.d.ts.map +1 -0
  98. package/dist/codegen-converter.js +464 -0
  99. package/dist/codegen-converter.js.map +1 -0
  100. package/dist/compression.d.ts +14 -0
  101. package/dist/compression.d.ts.map +1 -0
  102. package/dist/compression.js +150 -0
  103. package/dist/compression.js.map +1 -0
  104. package/dist/console-errors.d.ts +24 -0
  105. package/dist/console-errors.d.ts.map +1 -0
  106. package/dist/console-errors.js +96 -0
  107. package/dist/console-errors.js.map +1 -0
  108. package/dist/content-quality.d.ts +34 -0
  109. package/dist/content-quality.d.ts.map +1 -0
  110. package/dist/content-quality.js +124 -0
  111. package/dist/content-quality.js.map +1 -0
  112. package/dist/contract-openapi.d.ts +74 -0
  113. package/dist/contract-openapi.d.ts.map +1 -0
  114. package/dist/contract-openapi.js +305 -0
  115. package/dist/contract-openapi.js.map +1 -0
  116. package/dist/cookie-banner.d.ts +27 -0
  117. package/dist/cookie-banner.d.ts.map +1 -0
  118. package/dist/cookie-banner.js +285 -0
  119. package/dist/cookie-banner.js.map +1 -0
  120. package/dist/cookie-flags-audit.d.ts +35 -0
  121. package/dist/cookie-flags-audit.d.ts.map +1 -0
  122. package/dist/cookie-flags-audit.js +167 -0
  123. package/dist/cookie-flags-audit.js.map +1 -0
  124. package/dist/cpu-throttle.d.ts +34 -0
  125. package/dist/cpu-throttle.d.ts.map +1 -0
  126. package/dist/cpu-throttle.js +149 -0
  127. package/dist/cpu-throttle.js.map +1 -0
  128. package/dist/crawl.d.ts +29 -0
  129. package/dist/crawl.d.ts.map +1 -0
  130. package/dist/crawl.js +153 -0
  131. package/dist/crawl.js.map +1 -0
  132. package/dist/critical-css.d.ts +25 -0
  133. package/dist/critical-css.d.ts.map +1 -0
  134. package/dist/critical-css.js +353 -0
  135. package/dist/critical-css.js.map +1 -0
  136. package/dist/cross-browser.d.ts +44 -0
  137. package/dist/cross-browser.d.ts.map +1 -0
  138. package/dist/cross-browser.js +300 -0
  139. package/dist/cross-browser.js.map +1 -0
  140. package/dist/csrf-audit.d.ts +33 -0
  141. package/dist/csrf-audit.d.ts.map +1 -0
  142. package/dist/csrf-audit.js +276 -0
  143. package/dist/csrf-audit.js.map +1 -0
  144. package/dist/css-coverage.d.ts +20 -0
  145. package/dist/css-coverage.d.ts.map +1 -0
  146. package/dist/css-coverage.js +91 -0
  147. package/dist/css-coverage.js.map +1 -0
  148. package/dist/csv-exporter.d.ts +34 -0
  149. package/dist/csv-exporter.d.ts.map +1 -0
  150. package/dist/csv-exporter.js +241 -0
  151. package/dist/csv-exporter.js.map +1 -0
  152. package/dist/dark-mode-audit.d.ts +31 -0
  153. package/dist/dark-mode-audit.d.ts.map +1 -0
  154. package/dist/dark-mode-audit.js +236 -0
  155. package/dist/dark-mode-audit.js.map +1 -0
  156. package/dist/dead-images.d.ts +18 -0
  157. package/dist/dead-images.d.ts.map +1 -0
  158. package/dist/dead-images.js +236 -0
  159. package/dist/dead-images.js.map +1 -0
  160. package/dist/deadclicks.d.ts +19 -0
  161. package/dist/deadclicks.d.ts.map +1 -0
  162. package/dist/deadclicks.js +109 -0
  163. package/dist/deadclicks.js.map +1 -0
  164. package/dist/discord-formatter.d.ts +39 -0
  165. package/dist/discord-formatter.d.ts.map +1 -0
  166. package/dist/discord-formatter.js +191 -0
  167. package/dist/discord-formatter.js.map +1 -0
  168. package/dist/dom-audit.d.ts +23 -0
  169. package/dist/dom-audit.d.ts.map +1 -0
  170. package/dist/dom-audit.js +111 -0
  171. package/dist/dom-audit.js.map +1 -0
  172. package/dist/driver.d.ts.map +1 -1
  173. package/dist/driver.js +10 -0
  174. package/dist/driver.js.map +1 -1
  175. package/dist/error-page-audit.d.ts +26 -0
  176. package/dist/error-page-audit.d.ts.map +1 -0
  177. package/dist/error-page-audit.js +219 -0
  178. package/dist/error-page-audit.js.map +1 -0
  179. package/dist/event-listener-audit.d.ts +22 -0
  180. package/dist/event-listener-audit.d.ts.map +1 -0
  181. package/dist/event-listener-audit.js +156 -0
  182. package/dist/event-listener-audit.js.map +1 -0
  183. package/dist/exposed-paths.d.ts +21 -0
  184. package/dist/exposed-paths.d.ts.map +1 -0
  185. package/dist/exposed-paths.js +116 -0
  186. package/dist/exposed-paths.js.map +1 -0
  187. package/dist/favicon-audit.d.ts +28 -0
  188. package/dist/favicon-audit.d.ts.map +1 -0
  189. package/dist/favicon-audit.js +358 -0
  190. package/dist/favicon-audit.js.map +1 -0
  191. package/dist/flaky-detector.d.ts +32 -0
  192. package/dist/flaky-detector.d.ts.map +1 -0
  193. package/dist/flaky-detector.js +254 -0
  194. package/dist/flaky-detector.js.map +1 -0
  195. package/dist/flaky.d.ts +28 -0
  196. package/dist/flaky.d.ts.map +1 -0
  197. package/dist/flaky.js +106 -0
  198. package/dist/flaky.js.map +1 -0
  199. package/dist/focus-trap-audit.d.ts +29 -0
  200. package/dist/focus-trap-audit.d.ts.map +1 -0
  201. package/dist/focus-trap-audit.js +285 -0
  202. package/dist/focus-trap-audit.js.map +1 -0
  203. package/dist/font-loading.d.ts +29 -0
  204. package/dist/font-loading.d.ts.map +1 -0
  205. package/dist/font-loading.js +216 -0
  206. package/dist/font-loading.js.map +1 -0
  207. package/dist/forms-audit.d.ts +23 -0
  208. package/dist/forms-audit.d.ts.map +1 -0
  209. package/dist/forms-audit.js +147 -0
  210. package/dist/forms-audit.js.map +1 -0
  211. package/dist/github-annotations.d.ts +17 -0
  212. package/dist/github-annotations.d.ts.map +1 -0
  213. package/dist/github-annotations.js +264 -0
  214. package/dist/github-annotations.js.map +1 -0
  215. package/dist/graphql.d.ts +60 -0
  216. package/dist/graphql.d.ts.map +1 -0
  217. package/dist/graphql.js +188 -0
  218. package/dist/graphql.js.map +1 -0
  219. package/dist/har-waterfall.d.ts +37 -0
  220. package/dist/har-waterfall.d.ts.map +1 -0
  221. package/dist/har-waterfall.js +376 -0
  222. package/dist/har-waterfall.js.map +1 -0
  223. package/dist/heading-hierarchy.d.ts +20 -0
  224. package/dist/heading-hierarchy.d.ts.map +1 -0
  225. package/dist/heading-hierarchy.js +112 -0
  226. package/dist/heading-hierarchy.js.map +1 -0
  227. package/dist/headless-detect.d.ts +22 -0
  228. package/dist/headless-detect.d.ts.map +1 -0
  229. package/dist/headless-detect.js +167 -0
  230. package/dist/headless-detect.js.map +1 -0
  231. package/dist/history-timeline.d.ts +13 -0
  232. package/dist/history-timeline.d.ts.map +1 -0
  233. package/dist/history-timeline.js +327 -0
  234. package/dist/history-timeline.js.map +1 -0
  235. package/dist/hreflang-audit.d.ts +26 -0
  236. package/dist/hreflang-audit.d.ts.map +1 -0
  237. package/dist/hreflang-audit.js +273 -0
  238. package/dist/hreflang-audit.js.map +1 -0
  239. package/dist/hydration-audit.d.ts +21 -0
  240. package/dist/hydration-audit.d.ts.map +1 -0
  241. package/dist/hydration-audit.js +277 -0
  242. package/dist/hydration-audit.js.map +1 -0
  243. package/dist/image-audit.d.ts +41 -0
  244. package/dist/image-audit.d.ts.map +1 -0
  245. package/dist/image-audit.js +229 -0
  246. package/dist/image-audit.js.map +1 -0
  247. package/dist/index.d.ts +119 -0
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +708 -2
  250. package/dist/index.js.map +1 -1
  251. package/dist/init-wizard.d.ts +33 -0
  252. package/dist/init-wizard.d.ts.map +1 -0
  253. package/dist/init-wizard.js +289 -0
  254. package/dist/init-wizard.js.map +1 -0
  255. package/dist/inp-audit.d.ts +26 -0
  256. package/dist/inp-audit.d.ts.map +1 -0
  257. package/dist/inp-audit.js +202 -0
  258. package/dist/inp-audit.js.map +1 -0
  259. package/dist/js-coverage.d.ts +20 -0
  260. package/dist/js-coverage.d.ts.map +1 -0
  261. package/dist/js-coverage.js +81 -0
  262. package/dist/js-coverage.js.map +1 -0
  263. package/dist/json-schema.d.ts +27 -0
  264. package/dist/json-schema.d.ts.map +1 -0
  265. package/dist/json-schema.js +284 -0
  266. package/dist/json-schema.js.map +1 -0
  267. package/dist/keyboard.d.ts +21 -0
  268. package/dist/keyboard.d.ts.map +1 -0
  269. package/dist/keyboard.js +119 -0
  270. package/dist/keyboard.js.map +1 -0
  271. package/dist/lang-audit.d.ts +24 -0
  272. package/dist/lang-audit.d.ts.map +1 -0
  273. package/dist/lang-audit.js +141 -0
  274. package/dist/lang-audit.js.map +1 -0
  275. package/dist/lcp-element.d.ts +22 -0
  276. package/dist/lcp-element.d.ts.map +1 -0
  277. package/dist/lcp-element.js +240 -0
  278. package/dist/lcp-element.js.map +1 -0
  279. package/dist/longtasks.d.ts +38 -0
  280. package/dist/longtasks.d.ts.map +1 -0
  281. package/dist/longtasks.js +97 -0
  282. package/dist/longtasks.js.map +1 -0
  283. package/dist/mailbox.d.ts +35 -0
  284. package/dist/mailbox.d.ts.map +1 -0
  285. package/dist/mailbox.js +207 -0
  286. package/dist/mailbox.js.map +1 -0
  287. package/dist/media-audit.d.ts +20 -0
  288. package/dist/media-audit.d.ts.map +1 -0
  289. package/dist/media-audit.js +182 -0
  290. package/dist/media-audit.js.map +1 -0
  291. package/dist/metrics-exporter.d.ts +23 -0
  292. package/dist/metrics-exporter.d.ts.map +1 -0
  293. package/dist/metrics-exporter.js +297 -0
  294. package/dist/metrics-exporter.js.map +1 -0
  295. package/dist/mixed-content.d.ts +19 -0
  296. package/dist/mixed-content.d.ts.map +1 -0
  297. package/dist/mixed-content.js +86 -0
  298. package/dist/mixed-content.js.map +1 -0
  299. package/dist/motion-prefs.d.ts +21 -0
  300. package/dist/motion-prefs.d.ts.map +1 -0
  301. package/dist/motion-prefs.js +170 -0
  302. package/dist/motion-prefs.js.map +1 -0
  303. package/dist/open-graph.d.ts +40 -0
  304. package/dist/open-graph.d.ts.map +1 -0
  305. package/dist/open-graph.js +200 -0
  306. package/dist/open-graph.js.map +1 -0
  307. package/dist/orphan-assets.d.ts +17 -0
  308. package/dist/orphan-assets.d.ts.map +1 -0
  309. package/dist/orphan-assets.js +174 -0
  310. package/dist/orphan-assets.js.map +1 -0
  311. package/dist/page-object.d.ts +18 -0
  312. package/dist/page-object.d.ts.map +1 -0
  313. package/dist/page-object.js +346 -0
  314. package/dist/page-object.js.map +1 -0
  315. package/dist/pagination-audit.d.ts +24 -0
  316. package/dist/pagination-audit.d.ts.map +1 -0
  317. package/dist/pagination-audit.js +285 -0
  318. package/dist/pagination-audit.js.map +1 -0
  319. package/dist/passive-security.d.ts +19 -0
  320. package/dist/passive-security.d.ts.map +1 -0
  321. package/dist/passive-security.js +149 -0
  322. package/dist/passive-security.js.map +1 -0
  323. package/dist/pr-comment.d.ts +13 -0
  324. package/dist/pr-comment.d.ts.map +1 -0
  325. package/dist/pr-comment.js +316 -0
  326. package/dist/pr-comment.js.map +1 -0
  327. package/dist/precommit.d.ts +24 -0
  328. package/dist/precommit.d.ts.map +1 -0
  329. package/dist/precommit.js +239 -0
  330. package/dist/precommit.js.map +1 -0
  331. package/dist/prerender-audit.d.ts +22 -0
  332. package/dist/prerender-audit.d.ts.map +1 -0
  333. package/dist/prerender-audit.js +158 -0
  334. package/dist/prerender-audit.js.map +1 -0
  335. package/dist/print-audit.d.ts +21 -0
  336. package/dist/print-audit.d.ts.map +1 -0
  337. package/dist/print-audit.js +281 -0
  338. package/dist/print-audit.js.map +1 -0
  339. package/dist/protocol-audit.d.ts +17 -0
  340. package/dist/protocol-audit.d.ts.map +1 -0
  341. package/dist/protocol-audit.js +128 -0
  342. package/dist/protocol-audit.js.map +1 -0
  343. package/dist/reading-level.d.ts +37 -0
  344. package/dist/reading-level.d.ts.map +1 -0
  345. package/dist/reading-level.js +220 -0
  346. package/dist/reading-level.js.map +1 -0
  347. package/dist/redirects.d.ts +24 -0
  348. package/dist/redirects.d.ts.map +1 -0
  349. package/dist/redirects.js +119 -0
  350. package/dist/redirects.js.map +1 -0
  351. package/dist/report.d.ts +1 -1
  352. package/dist/report.d.ts.map +1 -1
  353. package/dist/report.js +736 -1
  354. package/dist/report.js.map +1 -1
  355. package/dist/reporter-plugin.d.ts +32 -0
  356. package/dist/reporter-plugin.d.ts.map +1 -0
  357. package/dist/reporter-plugin.js +120 -0
  358. package/dist/reporter-plugin.js.map +1 -0
  359. package/dist/resource-hints.d.ts +23 -0
  360. package/dist/resource-hints.d.ts.map +1 -0
  361. package/dist/resource-hints.js +225 -0
  362. package/dist/resource-hints.js.map +1 -0
  363. package/dist/retire.d.ts +22 -0
  364. package/dist/retire.d.ts.map +1 -0
  365. package/dist/retire.js +140 -0
  366. package/dist/retire.js.map +1 -0
  367. package/dist/retry.d.ts +20 -0
  368. package/dist/retry.d.ts.map +1 -0
  369. package/dist/retry.js +120 -0
  370. package/dist/retry.js.map +1 -0
  371. package/dist/robots-audit.d.ts +24 -0
  372. package/dist/robots-audit.d.ts.map +1 -0
  373. package/dist/robots-audit.js +206 -0
  374. package/dist/robots-audit.js.map +1 -0
  375. package/dist/rum.d.ts +35 -0
  376. package/dist/rum.d.ts.map +1 -0
  377. package/dist/rum.js +219 -0
  378. package/dist/rum.js.map +1 -0
  379. package/dist/schedule.d.ts +30 -0
  380. package/dist/schedule.d.ts.map +1 -0
  381. package/dist/schedule.js +238 -0
  382. package/dist/schedule.js.map +1 -0
  383. package/dist/secret-scan.d.ts +24 -0
  384. package/dist/secret-scan.d.ts.map +1 -0
  385. package/dist/secret-scan.js +202 -0
  386. package/dist/secret-scan.js.map +1 -0
  387. package/dist/service-worker.d.ts +26 -0
  388. package/dist/service-worker.d.ts.map +1 -0
  389. package/dist/service-worker.js +179 -0
  390. package/dist/service-worker.js.map +1 -0
  391. package/dist/shard.d.ts +14 -0
  392. package/dist/shard.d.ts.map +1 -0
  393. package/dist/shard.js +72 -0
  394. package/dist/shard.js.map +1 -0
  395. package/dist/sitemap-flows.d.ts +13 -0
  396. package/dist/sitemap-flows.d.ts.map +1 -0
  397. package/dist/sitemap-flows.js +157 -0
  398. package/dist/sitemap-flows.js.map +1 -0
  399. package/dist/sitemap.d.ts +27 -0
  400. package/dist/sitemap.d.ts.map +1 -0
  401. package/dist/sitemap.js +137 -0
  402. package/dist/sitemap.js.map +1 -0
  403. package/dist/slack-formatter.d.ts +35 -0
  404. package/dist/slack-formatter.d.ts.map +1 -0
  405. package/dist/slack-formatter.js +193 -0
  406. package/dist/slack-formatter.js.map +1 -0
  407. package/dist/sourcemap-scan.d.ts +24 -0
  408. package/dist/sourcemap-scan.d.ts.map +1 -0
  409. package/dist/sourcemap-scan.js +232 -0
  410. package/dist/sourcemap-scan.js.map +1 -0
  411. package/dist/sri-audit.d.ts +23 -0
  412. package/dist/sri-audit.d.ts.map +1 -0
  413. package/dist/sri-audit.js +180 -0
  414. package/dist/sri-audit.js.map +1 -0
  415. package/dist/storage-audit.d.ts +28 -0
  416. package/dist/storage-audit.d.ts.map +1 -0
  417. package/dist/storage-audit.js +263 -0
  418. package/dist/storage-audit.js.map +1 -0
  419. package/dist/storybook.d.ts +48 -0
  420. package/dist/storybook.d.ts.map +1 -0
  421. package/dist/storybook.js +191 -0
  422. package/dist/storybook.js.map +1 -0
  423. package/dist/structured-data.d.ts +25 -0
  424. package/dist/structured-data.d.ts.map +1 -0
  425. package/dist/structured-data.js +164 -0
  426. package/dist/structured-data.js.map +1 -0
  427. package/dist/svg-audit.d.ts +20 -0
  428. package/dist/svg-audit.d.ts.map +1 -0
  429. package/dist/svg-audit.js +213 -0
  430. package/dist/svg-audit.js.map +1 -0
  431. package/dist/table-audit.d.ts +18 -0
  432. package/dist/table-audit.d.ts.map +1 -0
  433. package/dist/table-audit.js +188 -0
  434. package/dist/table-audit.js.map +1 -0
  435. package/dist/teams-formatter.d.ts +66 -0
  436. package/dist/teams-formatter.d.ts.map +1 -0
  437. package/dist/teams-formatter.js +194 -0
  438. package/dist/teams-formatter.js.map +1 -0
  439. package/dist/third-party.d.ts +35 -0
  440. package/dist/third-party.d.ts.map +1 -0
  441. package/dist/third-party.js +175 -0
  442. package/dist/third-party.js.map +1 -0
  443. package/dist/tls.d.ts +33 -0
  444. package/dist/tls.d.ts.map +1 -0
  445. package/dist/tls.js +122 -0
  446. package/dist/tls.js.map +1 -0
  447. package/dist/touchtargets.d.ts +22 -0
  448. package/dist/touchtargets.d.ts.map +1 -0
  449. package/dist/touchtargets.js +80 -0
  450. package/dist/touchtargets.js.map +1 -0
  451. package/dist/tracker-sniff.d.ts +25 -0
  452. package/dist/tracker-sniff.d.ts.map +1 -0
  453. package/dist/tracker-sniff.js +355 -0
  454. package/dist/tracker-sniff.js.map +1 -0
  455. package/dist/types.d.ts +265 -1
  456. package/dist/types.d.ts.map +1 -1
  457. package/dist/visual-mask.d.ts +33 -0
  458. package/dist/visual-mask.d.ts.map +1 -0
  459. package/dist/visual-mask.js +102 -0
  460. package/dist/visual-mask.js.map +1 -0
  461. package/dist/visual-ssim.d.ts +26 -0
  462. package/dist/visual-ssim.d.ts.map +1 -0
  463. package/dist/visual-ssim.js +153 -0
  464. package/dist/visual-ssim.js.map +1 -0
  465. package/dist/watch-mode.d.ts +10 -0
  466. package/dist/watch-mode.d.ts.map +1 -0
  467. package/dist/watch-mode.js +156 -0
  468. package/dist/watch-mode.js.map +1 -0
  469. package/dist/web-worker-audit.d.ts +27 -0
  470. package/dist/web-worker-audit.d.ts.map +1 -0
  471. package/dist/web-worker-audit.js +324 -0
  472. package/dist/web-worker-audit.js.map +1 -0
  473. package/dist/webfonts.d.ts +26 -0
  474. package/dist/webfonts.d.ts.map +1 -0
  475. package/dist/webfonts.js +244 -0
  476. package/dist/webfonts.js.map +1 -0
  477. package/dist/webhook-reporter.d.ts +20 -0
  478. package/dist/webhook-reporter.d.ts.map +1 -0
  479. package/dist/webhook-reporter.js +124 -0
  480. package/dist/webhook-reporter.js.map +1 -0
  481. package/dist/websocket.d.ts +39 -0
  482. package/dist/websocket.d.ts.map +1 -0
  483. package/dist/websocket.js +233 -0
  484. package/dist/websocket.js.map +1 -0
  485. package/dist/worker-runtime.d.ts +129 -0
  486. package/dist/worker-runtime.d.ts.map +1 -0
  487. package/dist/worker-runtime.js +414 -0
  488. package/dist/worker-runtime.js.map +1 -0
  489. package/dist/zindex-audit.d.ts +28 -0
  490. package/dist/zindex-audit.d.ts.map +1 -0
  491. package/dist/zindex-audit.js +291 -0
  492. package/dist/zindex-audit.js.map +1 -0
  493. package/package.json +10 -2
@@ -0,0 +1,91 @@
1
+ const DEFAULT_THRESHOLD = 0.5;
2
+ const TOP_N = 30;
3
+ function emptyResult(pageUrl, passed) {
4
+ return {
5
+ page: pageUrl,
6
+ totalBytes: 0,
7
+ usedBytes: 0,
8
+ unusedBytes: 0,
9
+ unusedRatio: 0,
10
+ byFile: [],
11
+ passed,
12
+ };
13
+ }
14
+ export async function auditCssCoverage(page, opts) {
15
+ const pageUrl = page.url();
16
+ const threshold = opts?.threshold ?? DEFAULT_THRESHOLD;
17
+ const pageWithCoverage = page;
18
+ const coverage = pageWithCoverage.coverage;
19
+ if (!coverage || typeof coverage.startCSSCoverage !== 'function' || typeof coverage.stopCSSCoverage !== 'function') {
20
+ return emptyResult(pageUrl, true);
21
+ }
22
+ try {
23
+ await coverage.startCSSCoverage({ resetOnNavigation: false });
24
+ }
25
+ catch {
26
+ return emptyResult(pageUrl, true);
27
+ }
28
+ try {
29
+ await page.reload({ waitUntil: 'networkidle' });
30
+ }
31
+ catch {
32
+ try {
33
+ await coverage.stopCSSCoverage();
34
+ }
35
+ catch {
36
+ /* ignore */
37
+ }
38
+ return emptyResult(pageUrl, true);
39
+ }
40
+ let entries;
41
+ try {
42
+ entries = await coverage.stopCSSCoverage();
43
+ }
44
+ catch {
45
+ return emptyResult(pageUrl, true);
46
+ }
47
+ const merged = new Map();
48
+ for (const entry of entries) {
49
+ if (!entry.url || entry.url.startsWith('data:'))
50
+ continue;
51
+ const total = entry.text?.length ?? 0;
52
+ let used = 0;
53
+ for (const range of entry.ranges) {
54
+ const len = Math.max(0, range.end - range.start);
55
+ used += len;
56
+ }
57
+ if (total > 0 && used > total)
58
+ used = total;
59
+ const prev = merged.get(entry.url);
60
+ if (prev) {
61
+ merged.set(entry.url, { total: prev.total + total, used: prev.used + used });
62
+ }
63
+ else {
64
+ merged.set(entry.url, { total, used });
65
+ }
66
+ }
67
+ let totalBytes = 0;
68
+ let usedBytes = 0;
69
+ const files = [];
70
+ for (const [url, { total, used }] of merged) {
71
+ totalBytes += total;
72
+ usedBytes += used;
73
+ const unusedRatio = total > 0 ? Math.max(0, (total - used) / total) : 0;
74
+ files.push({ url, total, used, unusedRatio });
75
+ }
76
+ files.sort((a, b) => b.unusedRatio - a.unusedRatio);
77
+ const byFile = files.slice(0, TOP_N);
78
+ const unusedBytes = Math.max(0, totalBytes - usedBytes);
79
+ const unusedRatio = totalBytes > 0 ? unusedBytes / totalBytes : 0;
80
+ const passed = unusedRatio <= threshold;
81
+ return {
82
+ page: pageUrl,
83
+ totalBytes,
84
+ usedBytes,
85
+ unusedBytes,
86
+ unusedRatio,
87
+ byFile,
88
+ passed,
89
+ };
90
+ }
91
+ //# sourceMappingURL=css-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-coverage.js","sourceRoot":"","sources":["../src/css-coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { InspectResult } from './types.js';
2
+ export interface CsvOptions {
3
+ delimiter?: string;
4
+ lineEnding?: '\n' | '\r\n';
5
+ header?: boolean;
6
+ }
7
+ interface ResolvedCsvOptions {
8
+ delimiter: string;
9
+ lineEnding: '\n' | '\r\n';
10
+ header: boolean;
11
+ }
12
+ type CsvCell = string | number | boolean | null | undefined;
13
+ declare function resolve(opts: CsvOptions | undefined): ResolvedCsvOptions;
14
+ declare function escapeCell(value: CsvCell, delimiter: string): string;
15
+ declare function toRow(cells: CsvCell[], delimiter: string): string;
16
+ declare function avg(values: number[]): string;
17
+ declare function stepLabel(step: unknown): string;
18
+ export declare function flowsToCsv(result: InspectResult, opts?: CsvOptions): string;
19
+ export declare function a11yToCsv(result: InspectResult, opts?: CsvOptions): string;
20
+ export declare function perfToCsv(result: InspectResult, opts?: CsvOptions): string;
21
+ export declare function visualToCsv(result: InspectResult, opts?: CsvOptions): string;
22
+ export declare function linksToCsv(result: InspectResult, opts?: CsvOptions): string;
23
+ export declare function consoleErrorsToCsv(result: InspectResult, opts?: CsvOptions): string;
24
+ export declare function summaryToCsv(result: InspectResult, opts?: CsvOptions): string;
25
+ export declare function writeAllCsvs(result: InspectResult, outDir: string, opts?: CsvOptions): Promise<string[]>;
26
+ export declare const __internal: {
27
+ escapeCell: typeof escapeCell;
28
+ toRow: typeof toRow;
29
+ avg: typeof avg;
30
+ stepLabel: typeof stepLabel;
31
+ resolve: typeof resolve;
32
+ };
33
+ export {};
34
+ //# sourceMappingURL=csv-exporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-exporter.d.ts","sourceRoot":"","sources":["../src/csv-exporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,IAAI,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,KAAK,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;AAE5D,iBAAS,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,kBAAkB,CAMjE;AAED,iBAAS,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAe7D;AAED,iBAAS,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAaD,iBAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAKrC;AAED,iBAAS,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAMxC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAkB3E;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAU1E;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CA8B1E;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAQ5E;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAc3E;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,IAAI,CAAC,EAAE,UAAU,GAChB,MAAM,CAUR;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,EACrB,IAAI,CAAC,EAAE,UAAU,GAChB,MAAM,CAkER;AAUD,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAuBnB;AAGD,eAAO,MAAM,UAAU;;;;;;CAAiD,CAAC"}
@@ -0,0 +1,241 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ function resolve(opts) {
4
+ return {
5
+ delimiter: opts?.delimiter ?? ',',
6
+ lineEnding: opts?.lineEnding ?? '\n',
7
+ header: opts?.header !== false,
8
+ };
9
+ }
10
+ function escapeCell(value, delimiter) {
11
+ if (value === null || value === undefined)
12
+ return '';
13
+ if (typeof value === 'number') {
14
+ if (!Number.isFinite(value))
15
+ return '';
16
+ return String(value);
17
+ }
18
+ if (typeof value === 'boolean')
19
+ return value ? 'true' : 'false';
20
+ const s = String(value);
21
+ const needsQuote = s.includes(delimiter) ||
22
+ s.includes('"') ||
23
+ s.includes('\n') ||
24
+ s.includes('\r');
25
+ if (!needsQuote)
26
+ return s;
27
+ return `"${s.replace(/"/g, '""')}"`;
28
+ }
29
+ function toRow(cells, delimiter) {
30
+ return cells.map((c) => escapeCell(c, delimiter)).join(delimiter);
31
+ }
32
+ function serialize(headerRow, rows, opts) {
33
+ const lines = [];
34
+ if (opts.header)
35
+ lines.push(toRow(headerRow, opts.delimiter));
36
+ for (const r of rows)
37
+ lines.push(toRow(r, opts.delimiter));
38
+ return lines.join(opts.lineEnding) + (lines.length > 0 ? opts.lineEnding : '');
39
+ }
40
+ function avg(values) {
41
+ const valid = values.filter((v) => typeof v === 'number' && Number.isFinite(v));
42
+ if (valid.length === 0)
43
+ return '';
44
+ const sum = valid.reduce((a, b) => a + b, 0);
45
+ return (sum / valid.length).toFixed(2);
46
+ }
47
+ function stepLabel(step) {
48
+ if (step && typeof step === 'object') {
49
+ const keys = Object.keys(step);
50
+ if (keys.length > 0)
51
+ return keys[0];
52
+ }
53
+ return 'step';
54
+ }
55
+ export function flowsToCsv(result, opts) {
56
+ const o = resolve(opts);
57
+ const header = ['name', 'passed', 'steps', 'durationMs', 'error'];
58
+ const rows = [];
59
+ for (const f of result.flows ?? []) {
60
+ const durationMs = (f.steps ?? []).reduce((sum, s) => sum + (typeof s.durationMs === 'number' ? s.durationMs : 0), 0);
61
+ rows.push([
62
+ f.name,
63
+ f.passed,
64
+ (f.steps ?? []).length,
65
+ durationMs,
66
+ f.error ?? '',
67
+ ]);
68
+ }
69
+ return serialize(header, rows, o);
70
+ }
71
+ export function a11yToCsv(result, opts) {
72
+ const o = resolve(opts);
73
+ const header = ['page', 'id', 'impact', 'description', 'helpUrl'];
74
+ const rows = [];
75
+ for (const r of result.a11y ?? []) {
76
+ for (const v of r.violations ?? []) {
77
+ rows.push([r.page, v.id, v.impact, v.description, v.helpUrl]);
78
+ }
79
+ }
80
+ return serialize(header, rows, o);
81
+ }
82
+ export function perfToCsv(result, opts) {
83
+ const o = resolve(opts);
84
+ const header = [
85
+ 'page',
86
+ 'performance',
87
+ 'accessibility',
88
+ 'bestPractices',
89
+ 'seo',
90
+ 'lcp',
91
+ 'fcp',
92
+ 'cls',
93
+ 'tbt',
94
+ 'si',
95
+ ];
96
+ const rows = [];
97
+ for (const p of result.perf ?? []) {
98
+ rows.push([
99
+ p.page,
100
+ p.scores?.performance,
101
+ p.scores?.accessibility,
102
+ p.scores?.bestPractices,
103
+ p.scores?.seo,
104
+ p.metrics?.lcp,
105
+ p.metrics?.fcp,
106
+ p.metrics?.cls,
107
+ p.metrics?.tbt,
108
+ p.metrics?.si,
109
+ ]);
110
+ }
111
+ return serialize(header, rows, o);
112
+ }
113
+ export function visualToCsv(result, opts) {
114
+ const o = resolve(opts);
115
+ const header = ['page', 'viewport', 'diffPixels', 'diffRatio', 'passed'];
116
+ const rows = [];
117
+ for (const v of result.visual ?? []) {
118
+ rows.push([v.page, v.viewport, v.diffPixels, v.diffRatio, v.passed]);
119
+ }
120
+ return serialize(header, rows, o);
121
+ }
122
+ export function linksToCsv(result, opts) {
123
+ const o = resolve(opts);
124
+ const header = ['page', 'url', 'status', 'ok'];
125
+ const rows = [];
126
+ for (const link of result.links ?? []) {
127
+ for (const broken of link.broken ?? []) {
128
+ const ok = typeof broken.status === 'number' &&
129
+ broken.status >= 200 &&
130
+ broken.status < 400;
131
+ rows.push([link.page, broken.url, broken.status, ok]);
132
+ }
133
+ }
134
+ return serialize(header, rows, o);
135
+ }
136
+ export function consoleErrorsToCsv(result, opts) {
137
+ const o = resolve(opts);
138
+ const header = ['page', 'type', 'message'];
139
+ const rows = [];
140
+ for (const c of result.consoleErrors ?? []) {
141
+ for (const issue of c.issues ?? []) {
142
+ rows.push([c.page, issue.type, issue.message]);
143
+ }
144
+ }
145
+ return serialize(header, rows, o);
146
+ }
147
+ export function summaryToCsv(result, opts) {
148
+ const o = resolve(opts);
149
+ const header = [
150
+ 'url',
151
+ 'startedAt',
152
+ 'passed',
153
+ 'durationMs',
154
+ 'flowsTotal',
155
+ 'flowsFailed',
156
+ 'a11yCritical',
157
+ 'a11ySerious',
158
+ 'lcpAvg',
159
+ 'clsAvg',
160
+ 'visualDiffs',
161
+ 'brokenLinks',
162
+ 'consoleErrors',
163
+ ];
164
+ const flows = result.flows ?? [];
165
+ const flowsTotal = flows.length;
166
+ const flowsFailed = flows.filter((f) => !f.passed).length;
167
+ let a11yCritical = 0;
168
+ let a11ySerious = 0;
169
+ for (const r of result.a11y ?? []) {
170
+ for (const v of r.violations ?? []) {
171
+ if (v.impact === 'critical')
172
+ a11yCritical += 1;
173
+ else if (v.impact === 'serious')
174
+ a11ySerious += 1;
175
+ }
176
+ }
177
+ const perf = result.perf ?? [];
178
+ const lcpAvg = avg(perf.map((p) => p.metrics?.lcp ?? NaN));
179
+ const clsAvg = avg(perf.map((p) => p.metrics?.cls ?? NaN));
180
+ const visualDiffs = (result.visual ?? []).filter((v) => !v.passed || v.diffPixels > 0).length;
181
+ let brokenLinks = 0;
182
+ for (const link of result.links ?? []) {
183
+ brokenLinks += (link.broken ?? []).length;
184
+ }
185
+ let consoleErrorCount = 0;
186
+ for (const c of result.consoleErrors ?? []) {
187
+ consoleErrorCount += c.errorCount ?? 0;
188
+ }
189
+ const row = [
190
+ result.url,
191
+ result.startedAt,
192
+ result.passed,
193
+ result.durationMs,
194
+ flowsTotal,
195
+ flowsFailed,
196
+ a11yCritical,
197
+ a11ySerious,
198
+ lcpAvg,
199
+ clsAvg,
200
+ visualDiffs,
201
+ brokenLinks,
202
+ consoleErrorCount,
203
+ ];
204
+ return serialize(header, [row], o);
205
+ }
206
+ function hasBodyRows(csv, opts) {
207
+ if (!csv)
208
+ return false;
209
+ const parts = csv.split(opts.lineEnding).filter((l) => l.length > 0);
210
+ if (parts.length === 0)
211
+ return false;
212
+ if (opts.header)
213
+ return parts.length > 1;
214
+ return parts.length > 0;
215
+ }
216
+ export async function writeAllCsvs(result, outDir, opts) {
217
+ const o = resolve(opts);
218
+ await mkdir(outDir, { recursive: true });
219
+ const sections = [
220
+ { name: 'flows.csv', csv: flowsToCsv(result, opts) },
221
+ { name: 'a11y.csv', csv: a11yToCsv(result, opts) },
222
+ { name: 'perf.csv', csv: perfToCsv(result, opts) },
223
+ { name: 'visual.csv', csv: visualToCsv(result, opts) },
224
+ { name: 'links.csv', csv: linksToCsv(result, opts) },
225
+ { name: 'console-errors.csv', csv: consoleErrorsToCsv(result, opts) },
226
+ { name: 'summary.csv', csv: summaryToCsv(result, opts), always: true },
227
+ ];
228
+ const written = [];
229
+ for (const s of sections) {
230
+ const include = s.always === true || hasBodyRows(s.csv, o);
231
+ if (!include)
232
+ continue;
233
+ const path = join(outDir, s.name);
234
+ await writeFile(path, s.csv, 'utf8');
235
+ written.push(path);
236
+ }
237
+ return written;
238
+ }
239
+ // internal exports used by tests / adjacent tooling
240
+ export const __internal = { escapeCell, toRow, avg, stepLabel, resolve };
241
+ //# sourceMappingURL=csv-exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-exporter.js","sourceRoot":"","sources":["../src/csv-exporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { Page, BrowserContext } from 'playwright';
2
+ export interface DarkModeIssue {
3
+ type: 'no-color-scheme-meta' | 'no-media-query-support' | 'visually-identical' | 'contrast-regression' | 'hardcoded-color' | 'missing-theme-color-dark';
4
+ severity: 'warn' | 'error';
5
+ selector?: string;
6
+ detail: string;
7
+ }
8
+ export interface DarkModeResult {
9
+ page: string;
10
+ supportsColorScheme: boolean;
11
+ hasColorSchemeMeta: boolean;
12
+ hasThemeColorLight: boolean;
13
+ hasThemeColorDark: boolean;
14
+ lightScreenshot?: string;
15
+ darkScreenshot?: string;
16
+ pixelDiffRatio: number;
17
+ contrastRegressions: Array<{
18
+ selector: string;
19
+ lightRatio: number;
20
+ darkRatio: number;
21
+ }>;
22
+ issues: DarkModeIssue[];
23
+ passed: boolean;
24
+ }
25
+ export interface DarkModeAuditOptions {
26
+ screenshotDir?: string;
27
+ sampleSelectors?: string[];
28
+ }
29
+ export type DarkModeContext = BrowserContext;
30
+ export declare function auditDarkMode(page: Page, opts?: DarkModeAuditOptions): Promise<DarkModeResult>;
31
+ //# sourceMappingURL=dark-mode-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dark-mode-audit.d.ts","sourceRoot":"","sources":["../src/dark-mode-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIvD,MAAM,WAAW,aAAa;IAC5B,IAAI,EACA,sBAAsB,GACtB,wBAAwB,GACxB,oBAAoB,GACpB,qBAAqB,GACrB,iBAAiB,GACjB,0BAA0B,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,OAAO,CAAC;IAC7B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAGD,MAAM,MAAM,eAAe,GAAG,cAAc,CAAC;AA8J7C,wBAAsB,aAAa,CACjC,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE,oBAAoB,GAC1B,OAAO,CAAC,cAAc,CAAC,CA2EzB"}
@@ -0,0 +1,236 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ const DEFAULT_SELECTORS = ['body', 'main', 'h1', 'p', 'a', 'button'];
4
+ const PIXEL_DIFF_THRESHOLD = 0.02;
5
+ const WCAG_AA_NORMAL = 4.5;
6
+ const HARDCODED_LIMIT = 20;
7
+ function parseColor(input) {
8
+ const s = input.trim().toLowerCase();
9
+ if (!s || s === 'transparent' || s === 'currentcolor')
10
+ return null;
11
+ const rgb = s.match(/^rgba?\s*\(\s*([\d.]+)[ ,]+([\d.]+)[ ,]+([\d.]+)(?:[ ,/]+([\d.%]+))?\s*\)$/);
12
+ if (rgb) {
13
+ const a = rgb[4] === undefined ? 1 : rgb[4].endsWith('%') ? Number(rgb[4].slice(0, -1)) / 100 : Number(rgb[4]);
14
+ return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3]), a];
15
+ }
16
+ const hex = s.match(/^#([0-9a-f]{3,8})$/);
17
+ if (!hex)
18
+ return null;
19
+ const h = hex[1];
20
+ const expand = (i, n) => parseInt(h.slice(i, i + n).padEnd(2, h.slice(i, i + n)), 16);
21
+ if (h.length === 3 || h.length === 4) {
22
+ const a = h.length === 4 ? parseInt(h[3] + h[3], 16) / 255 : 1;
23
+ return [expand(0, 1), expand(1, 1), expand(2, 1), a];
24
+ }
25
+ if (h.length === 6 || h.length === 8) {
26
+ const a = h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1;
27
+ return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16), a];
28
+ }
29
+ return null;
30
+ }
31
+ function relLum(c) {
32
+ const v = c / 255;
33
+ return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
34
+ }
35
+ function luminance(rgb) {
36
+ return 0.2126 * relLum(rgb[0]) + 0.7152 * relLum(rgb[1]) + 0.0722 * relLum(rgb[2]);
37
+ }
38
+ function contrastRatio(fgStr, bgStr) {
39
+ const fg = parseColor(fgStr);
40
+ const bg = parseColor(bgStr);
41
+ if (!fg || !bg)
42
+ return null;
43
+ const a = fg[3];
44
+ const blended = a < 1
45
+ ? [fg[0] * a + bg[0] * (1 - a), fg[1] * a + bg[1] * (1 - a), fg[2] * a + bg[2] * (1 - a)]
46
+ : [fg[0], fg[1], fg[2]];
47
+ const l1 = luminance(blended);
48
+ const l2 = luminance([bg[0], bg[1], bg[2]]);
49
+ const hi = Math.max(l1, l2);
50
+ const lo = Math.min(l1, l2);
51
+ return (hi + 0.05) / (lo + 0.05);
52
+ }
53
+ function diffRatio(a, b) {
54
+ if (a.length === 0 || b.length === 0)
55
+ return 0;
56
+ const len = Math.min(a.length, b.length);
57
+ let sampled = 0;
58
+ let diff = 0;
59
+ for (let i = 0; i < len; i += 8) {
60
+ sampled += 1;
61
+ if (a[i] !== b[i])
62
+ diff += 1;
63
+ }
64
+ return sampled === 0 ? 0 : diff / sampled;
65
+ }
66
+ async function scanMeta(page) {
67
+ return await page.evaluate(() => {
68
+ const cs = document.querySelector('meta[name="color-scheme"]');
69
+ const themes = Array.from(document.querySelectorAll('meta[name="theme-color"]'));
70
+ let hasLight = false;
71
+ let hasDark = false;
72
+ for (const m of themes) {
73
+ const media = (m.getAttribute('media') ?? '').toLowerCase();
74
+ if (media.includes('prefers-color-scheme: dark'))
75
+ hasDark = true;
76
+ else
77
+ hasLight = true;
78
+ }
79
+ return { hasColorSchemeMeta: cs !== null, hasThemeColorLight: hasLight, hasThemeColorDark: hasDark };
80
+ });
81
+ }
82
+ async function scanStylesheets(page) {
83
+ return await page.evaluate((limit) => {
84
+ let supportsColorScheme = false;
85
+ const hardcoded = [];
86
+ const commonRx = /(^|[\s,>+~])(html|body|main|a|p|h[1-6])(\s*[,{:.#\[]|$)/i;
87
+ const literalRx = /#[0-9a-f]{3,8}\b|\brgba?\s*\(/i;
88
+ const varRx = /\bvar\s*\(\s*--/i;
89
+ function inspect(rule) {
90
+ if (hardcoded.length >= limit)
91
+ return;
92
+ const sel = rule.selectorText || '';
93
+ if (!commonRx.test(sel))
94
+ return;
95
+ const style = rule.style;
96
+ const candidates = [
97
+ style.getPropertyValue('color').trim(),
98
+ style.getPropertyValue('background-color').trim(),
99
+ style.getPropertyValue('background').trim(),
100
+ ].filter(v => v.length > 0);
101
+ for (const value of candidates) {
102
+ if (varRx.test(value))
103
+ continue;
104
+ if (literalRx.test(value)) {
105
+ hardcoded.push({ selector: sel, detail: `literal color in rule: ${value.slice(0, 80)}` });
106
+ break;
107
+ }
108
+ }
109
+ }
110
+ function walk(rules) {
111
+ if (!rules)
112
+ return;
113
+ for (let i = 0; i < rules.length; i += 1) {
114
+ const rule = rules[i];
115
+ if (rule instanceof CSSMediaRule) {
116
+ const text = (rule.conditionText || rule.media.mediaText || '').toLowerCase();
117
+ if (text.includes('prefers-color-scheme: dark'))
118
+ supportsColorScheme = true;
119
+ walk(rule.cssRules);
120
+ continue;
121
+ }
122
+ if (rule instanceof CSSStyleRule) {
123
+ inspect(rule);
124
+ continue;
125
+ }
126
+ const nested = rule;
127
+ if (nested.cssRules)
128
+ walk(nested.cssRules);
129
+ }
130
+ }
131
+ for (const sheet of Array.from(document.styleSheets)) {
132
+ try {
133
+ walk(sheet.cssRules);
134
+ }
135
+ catch { /* cross-origin */ }
136
+ }
137
+ return { supportsColorScheme, hardcodedRules: hardcoded };
138
+ }, HARDCODED_LIMIT);
139
+ }
140
+ async function readSelectorColors(page, selectors) {
141
+ return await page.evaluate((sels) => {
142
+ function effectiveBg(el) {
143
+ let cur = el;
144
+ while (cur) {
145
+ const bg = window.getComputedStyle(cur).backgroundColor;
146
+ if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent')
147
+ return bg;
148
+ cur = cur.parentElement;
149
+ }
150
+ return window.getComputedStyle(document.documentElement).backgroundColor || 'rgb(255, 255, 255)';
151
+ }
152
+ return sels.map(sel => {
153
+ try {
154
+ const el = document.querySelector(sel);
155
+ if (!el)
156
+ return { selector: sel, found: false, fg: '', bg: '' };
157
+ const cs = window.getComputedStyle(el);
158
+ return { selector: sel, found: true, fg: cs.color, bg: effectiveBg(el) };
159
+ }
160
+ catch {
161
+ return { selector: sel, found: false, fg: '', bg: '' };
162
+ }
163
+ });
164
+ }, selectors);
165
+ }
166
+ export async function auditDarkMode(page, opts) {
167
+ const url = page.url();
168
+ const sampleSelectors = opts?.sampleSelectors ?? DEFAULT_SELECTORS;
169
+ const issues = [];
170
+ const meta = await scanMeta(page);
171
+ const styles = await scanStylesheets(page);
172
+ await page.emulateMedia({ colorScheme: 'light' });
173
+ const lightBytes = await page.screenshot({ type: 'png', fullPage: false });
174
+ const lightColors = await readSelectorColors(page, sampleSelectors);
175
+ await page.emulateMedia({ colorScheme: 'dark' });
176
+ const darkBytes = await page.screenshot({ type: 'png', fullPage: false });
177
+ const darkColors = await readSelectorColors(page, sampleSelectors);
178
+ let lightScreenshot;
179
+ let darkScreenshot;
180
+ if (opts?.screenshotDir) {
181
+ await fs.mkdir(opts.screenshotDir, { recursive: true });
182
+ lightScreenshot = path.join(opts.screenshotDir, 'dark-mode-light.png');
183
+ darkScreenshot = path.join(opts.screenshotDir, 'dark-mode-dark.png');
184
+ await fs.writeFile(lightScreenshot, lightBytes);
185
+ await fs.writeFile(darkScreenshot, darkBytes);
186
+ }
187
+ const pixelDiffRatio = diffRatio(lightBytes, darkBytes);
188
+ await page.emulateMedia({ colorScheme: null });
189
+ if (!meta.hasColorSchemeMeta) {
190
+ issues.push({ type: 'no-color-scheme-meta', severity: 'warn', detail: 'Missing <meta name="color-scheme">; browsers cannot pre-style scrollbars or form controls.' });
191
+ }
192
+ if (!styles.supportsColorScheme) {
193
+ issues.push({ type: 'no-media-query-support', severity: 'error', detail: 'No @media (prefers-color-scheme: dark) rules found in any same-origin stylesheet.' });
194
+ }
195
+ if (meta.hasThemeColorLight && !meta.hasThemeColorDark) {
196
+ issues.push({ type: 'missing-theme-color-dark', severity: 'warn', detail: 'theme-color defined for light only; add a media="(prefers-color-scheme: dark)" variant.' });
197
+ }
198
+ if (styles.supportsColorScheme && pixelDiffRatio < PIXEL_DIFF_THRESHOLD) {
199
+ issues.push({ type: 'visually-identical', severity: 'warn', detail: `Light vs dark differ by only ${(pixelDiffRatio * 100).toFixed(2)}%; dark mode rules may not actually apply.` });
200
+ }
201
+ for (const rule of styles.hardcodedRules) {
202
+ issues.push({ type: 'hardcoded-color', severity: 'warn', selector: rule.selector, detail: rule.detail });
203
+ }
204
+ const contrastRegressions = [];
205
+ for (let i = 0; i < sampleSelectors.length; i += 1) {
206
+ const sel = sampleSelectors[i];
207
+ const light = lightColors[i];
208
+ const dark = darkColors[i];
209
+ if (!light?.found || !dark?.found)
210
+ continue;
211
+ const lightRatio = contrastRatio(light.fg, light.bg);
212
+ const darkRatio = contrastRatio(dark.fg, dark.bg);
213
+ if (lightRatio === null || darkRatio === null)
214
+ continue;
215
+ if (darkRatio < lightRatio && darkRatio < WCAG_AA_NORMAL) {
216
+ const lr = Number(lightRatio.toFixed(2));
217
+ const dr = Number(darkRatio.toFixed(2));
218
+ contrastRegressions.push({ selector: sel, lightRatio: lr, darkRatio: dr });
219
+ issues.push({ type: 'contrast-regression', severity: 'error', selector: sel, detail: `Contrast drops from ${lr} (light) to ${dr} (dark); below WCAG AA 4.5:1.` });
220
+ }
221
+ }
222
+ return {
223
+ page: url,
224
+ supportsColorScheme: styles.supportsColorScheme,
225
+ hasColorSchemeMeta: meta.hasColorSchemeMeta,
226
+ hasThemeColorLight: meta.hasThemeColorLight,
227
+ hasThemeColorDark: meta.hasThemeColorDark,
228
+ lightScreenshot,
229
+ darkScreenshot,
230
+ pixelDiffRatio: Number(pixelDiffRatio.toFixed(4)),
231
+ contrastRegressions,
232
+ issues,
233
+ passed: issues.every(i => i.severity !== 'error'),
234
+ };
235
+ }
236
+ //# sourceMappingURL=dark-mode-audit.js.map