@hawkeyexl/manni 4.5.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/batch-U6S2JUUN.js +41 -0
- package/dist/build-4RN6WBCP.js +32 -0
- package/dist/calibrate-6VIZYR4L.js +36 -0
- package/dist/capture-DP3NXAZE.js +34 -0
- package/dist/{check-RQIIIURI.js → check-ERVNGKLC.js} +13 -14
- package/dist/check-IWHUPAZM.js +886 -0
- package/dist/check-IWHUPAZM.js.map +1 -0
- package/dist/{check-ESNS7ZBC.js → check-OAJWC3WD.js} +7 -5
- package/dist/check-STHXZIZO.js +26 -0
- package/dist/{chunk-TA42STP7.js → chunk-2ZWTSRE5.js} +4 -4
- package/dist/{chunk-F2OVHJNC.js → chunk-4Z2MN5FR.js} +11 -11
- package/dist/{chunk-HVFR66GQ.js → chunk-5UB47BEV.js} +3280 -283
- package/dist/chunk-5UB47BEV.js.map +1 -0
- package/dist/{chunk-TCC737IS.js → chunk-5VWDIWOH.js} +4 -4
- package/dist/{chunk-B7XUEQF3.js → chunk-6KKZXB2O.js} +7 -5
- package/dist/{chunk-B7XUEQF3.js.map → chunk-6KKZXB2O.js.map} +1 -1
- package/dist/{chunk-ELZ2IIOF.js → chunk-6URSCTTX.js} +2 -2
- package/dist/{chunk-K3U5F2QT.js → chunk-6VDBP5UV.js} +12 -12
- package/dist/chunk-7EL5LLKD.js +233 -0
- package/dist/chunk-7EL5LLKD.js.map +1 -0
- package/dist/{chunk-C3KI45RF.js → chunk-7XZ6INKO.js} +4 -4
- package/dist/{chunk-ZX43SRZR.js → chunk-AMEILOVV.js} +8 -6
- package/dist/{chunk-ZX43SRZR.js.map → chunk-AMEILOVV.js.map} +1 -1
- package/dist/{chunk-SNEHX4VE.js → chunk-B5CMM57M.js} +2 -2
- package/dist/chunk-BWCWTHYS.js +146 -0
- package/dist/chunk-BWCWTHYS.js.map +1 -0
- package/dist/{chunk-QLUMKWQQ.js → chunk-C2TJG5KV.js} +10 -6
- package/dist/{chunk-QLUMKWQQ.js.map → chunk-C2TJG5KV.js.map} +1 -1
- package/dist/{chunk-GAZFKZHM.js → chunk-CEVU3GFF.js} +6 -6
- package/dist/chunk-CI55ABCK.js +502 -0
- package/dist/chunk-CI55ABCK.js.map +1 -0
- package/dist/{chunk-QCBICNKC.js → chunk-CR3HZERW.js} +202 -54
- package/dist/chunk-CR3HZERW.js.map +1 -0
- package/dist/chunk-CU57HT2E.js +116 -0
- package/dist/chunk-CU57HT2E.js.map +1 -0
- package/dist/chunk-CWXZ2MRT.js +222 -0
- package/dist/chunk-CWXZ2MRT.js.map +1 -0
- package/dist/chunk-D2LBN44V.js +134 -0
- package/dist/chunk-D2LBN44V.js.map +1 -0
- package/dist/chunk-D6RO54QD.js +674 -0
- package/dist/chunk-D6RO54QD.js.map +1 -0
- package/dist/{chunk-52PDIZKV.js → chunk-ECVQNGTH.js} +3 -3
- package/dist/chunk-FQGLRJGX.js +174 -0
- package/dist/chunk-FQGLRJGX.js.map +1 -0
- package/dist/{chunk-NGK3N6F2.js → chunk-GCC52YZD.js} +3 -3
- package/dist/chunk-HX44GB4Z.js +30 -0
- package/dist/chunk-HX44GB4Z.js.map +1 -0
- package/dist/{chunk-NN4ASVTY.js → chunk-IIFADWWI.js} +4 -4
- package/dist/{chunk-OIZ3XMKB.js → chunk-IOL74S4J.js} +4 -4
- package/dist/{chunk-UK2I3ACY.js → chunk-IZBBXKMJ.js} +4 -4
- package/dist/{chunk-YA63PVWI.js → chunk-K52CS4KT.js} +12 -10
- package/dist/{chunk-YA63PVWI.js.map → chunk-K52CS4KT.js.map} +1 -1
- package/dist/{chunk-7OQAU4EA.js → chunk-LDKIIZDW.js} +16 -12
- package/dist/{chunk-7OQAU4EA.js.map → chunk-LDKIIZDW.js.map} +1 -1
- package/dist/{chunk-QY3DOZMS.js → chunk-LONRQBGV.js} +4 -4
- package/dist/{chunk-EL2BZTDL.js → chunk-LXJEAV2V.js} +13 -11
- package/dist/{chunk-EL2BZTDL.js.map → chunk-LXJEAV2V.js.map} +1 -1
- package/dist/{chunk-XWFNO5PO.js → chunk-MIBWI5H7.js} +3 -3
- package/dist/{chunk-527HHQPS.js → chunk-MSMB6XOS.js} +6 -4
- package/dist/{chunk-527HHQPS.js.map → chunk-MSMB6XOS.js.map} +1 -1
- package/dist/{chunk-C6HZ6C5T.js → chunk-NIFEEIGQ.js} +2 -2
- package/dist/{chunk-JQU7FHYH.js → chunk-NZ6TNSCU.js} +10 -10
- package/dist/{chunk-3R5Y3XOL.js → chunk-OOEMKOX6.js} +29 -591
- package/dist/chunk-OOEMKOX6.js.map +1 -0
- package/dist/{chunk-MZC62HPZ.js → chunk-PAOVVHQX.js} +2 -2
- package/dist/chunk-PNX43DYW.js +849 -0
- package/dist/chunk-PNX43DYW.js.map +1 -0
- package/dist/chunk-QCHVR4ZM.js +286 -0
- package/dist/chunk-QCHVR4ZM.js.map +1 -0
- package/dist/{chunk-SJO2FTPG.js → chunk-QP5X66TO.js} +73 -1304
- package/dist/chunk-QP5X66TO.js.map +1 -0
- package/dist/{chunk-HQFVJDEN.js → chunk-QQNPVW3J.js} +19 -19
- package/dist/chunk-TPVOOTMN.js +532 -0
- package/dist/chunk-TPVOOTMN.js.map +1 -0
- package/dist/{chunk-IA7BMIPE.js → chunk-UZKQSWHJ.js} +2 -2
- package/dist/chunk-WQWKYAAK.js +86 -0
- package/dist/chunk-WQWKYAAK.js.map +1 -0
- package/dist/{chunk-CP4ZM43V.js → chunk-WTEILBKC.js} +2 -2
- package/dist/{chunk-LJ2RDH3F.js → chunk-XG6PVB5S.js} +1 -23
- package/dist/chunk-XG6PVB5S.js.map +1 -0
- package/dist/{chunk-Q35YIDBV.js → chunk-XKBEHKWO.js} +29 -264
- package/dist/chunk-XKBEHKWO.js.map +1 -0
- package/dist/{chunk-BOIF2XGG.js → chunk-XXDXIIQ4.js} +2 -2
- package/dist/{chunk-6XTQDFGP.js → chunk-YSCZL4U5.js} +3 -3
- package/dist/{chunk-6XTQDFGP.js.map → chunk-YSCZL4U5.js.map} +1 -1
- package/dist/{chunk-7ZUCEMCY.js → chunk-ZEXGGGLW.js} +9 -11
- package/dist/{chunk-7ZUCEMCY.js.map → chunk-ZEXGGGLW.js.map} +1 -1
- package/dist/{chunk-DX32P2DT.js → chunk-ZKAKA54B.js} +2 -2
- package/dist/{chunk-TVIX653Q.js → chunk-ZKCNPKUO.js} +4 -4
- package/dist/{chunk-DE7JSWXV.js → chunk-ZNE25EAR.js} +8 -6
- package/dist/{chunk-DE7JSWXV.js.map → chunk-ZNE25EAR.js.map} +1 -1
- package/dist/{chunk-FLF6AECQ.js → chunk-ZPP75QWG.js} +17 -15
- package/dist/{chunk-FLF6AECQ.js.map → chunk-ZPP75QWG.js.map} +1 -1
- package/dist/{cli-PRZHQ3QB.js → cli-37SRAYNE.js} +14 -15
- package/dist/{cli-PRZHQ3QB.js.map → cli-37SRAYNE.js.map} +1 -1
- package/dist/{cli-TYGOHJOJ.js → cli-HXWYUKGJ.js} +225 -58
- package/dist/cli-HXWYUKGJ.js.map +1 -0
- package/dist/{cli-HUIMNJT6.js → cli-IRHPPUFJ.js} +19 -20
- package/dist/{cli-HUIMNJT6.js.map → cli-IRHPPUFJ.js.map} +1 -1
- package/dist/{cli-6PGVHP7Y.js → cli-KIID535Y.js} +17 -19
- package/dist/{cli-6PGVHP7Y.js.map → cli-KIID535Y.js.map} +1 -1
- package/dist/{cli-AAG3N4NB.js → cli-MTDGQIIV.js} +17 -18
- package/dist/{cli-AAG3N4NB.js.map → cli-MTDGQIIV.js.map} +1 -1
- package/dist/{cli-E3H6ZP4I.js → cli-NM6FZ54R.js} +5 -3
- package/dist/{cli-E3H6ZP4I.js.map → cli-NM6FZ54R.js.map} +1 -1
- package/dist/{cli-MHTYPVOD.js → cli-XIODOEUG.js} +113 -20
- package/dist/{cli-MHTYPVOD.js.map → cli-XIODOEUG.js.map} +1 -1
- package/dist/{cli-YW6VWBHX.js → cli-YL3IDR52.js} +10 -10
- package/dist/{cli-QKIJOXGW.js → cli-ZQUJYQBA.js} +17 -17
- package/dist/cli.js +11 -11
- package/dist/config-O6UBGEDK.js +26 -0
- package/dist/conformance-ALLPNGEQ.js +18 -0
- package/dist/docmeta.js +9 -9
- package/dist/github-UCYLM3DR.js +26 -0
- package/dist/github-VEJFO6JJ.js +31 -0
- package/dist/host-6GURH5HE.js +23 -0
- package/dist/index.d.ts +72 -4
- package/dist/index.js +176 -166
- package/dist/index.js.map +1 -1
- package/dist/{json-4DCKDGUV.js → json-6F52FSXZ.js} +12 -13
- package/dist/lint-X2GNINK6.js +29 -0
- package/dist/list-DP3O3CWB.js +30 -0
- package/dist/list-P77RZFZF.js +28 -0
- package/dist/{page-QEDB7EVU.js → page-WQCMBNRD.js} +11 -12
- package/dist/prepare-52QOK7M4.js +155 -0
- package/dist/prepare-52QOK7M4.js.map +1 -0
- package/dist/{pretty-SQX33UM2.js → pretty-5MMMNSWF.js} +13 -14
- package/dist/{pretty-5DGEBZSV.js → pretty-H4DQREMW.js} +12 -13
- package/dist/provider-AMGRDY6V.js +34 -0
- package/dist/release-CTYRHZL4.js +36 -0
- package/dist/release-CTYRHZL4.js.map +1 -0
- package/dist/{reporters-YELDDYJD.js → reporters-SU3VUKKX.js} +7 -5
- package/dist/{reporters-OHSI3RMV.js → reporters-WIHBEMPG.js} +11 -12
- package/dist/reporters-WIHBEMPG.js.map +1 -0
- package/dist/run-L2DWJ26K.js +32 -0
- package/dist/run-L2DWJ26K.js.map +1 -0
- package/dist/{term-2WMDZ4PY.js → term-PJIJQZNF.js} +16 -17
- package/dist/{term-2WMDZ4PY.js.map → term-PJIJQZNF.js.map} +1 -1
- package/dist/validate-XCDSPURG.js +17 -0
- package/dist/validate-XCDSPURG.js.map +1 -0
- package/package.json +2 -2
- package/plugin/manni/.claude-plugin/plugin.json +1 -1
- package/plugin/manni/hooks/hooks.json +47 -0
- package/dist/batch-GOE3UXOD.js +0 -36
- package/dist/build-M7BMEHL2.js +0 -33
- package/dist/calibrate-6RRAILUY.js +0 -31
- package/dist/capture-RJPQ6B4N.js +0 -32
- package/dist/check-BQD64X3N.js +0 -27
- package/dist/chunk-3R5Y3XOL.js.map +0 -1
- package/dist/chunk-3YHAYXBC.js +0 -164
- package/dist/chunk-3YHAYXBC.js.map +0 -1
- package/dist/chunk-HVFR66GQ.js.map +0 -1
- package/dist/chunk-LJ2RDH3F.js.map +0 -1
- package/dist/chunk-NAZRLJAK.js +0 -3090
- package/dist/chunk-NAZRLJAK.js.map +0 -1
- package/dist/chunk-Q35YIDBV.js.map +0 -1
- package/dist/chunk-QCBICNKC.js.map +0 -1
- package/dist/chunk-SJO2FTPG.js.map +0 -1
- package/dist/chunk-TU2WUBVZ.js +0 -2
- package/dist/cli-TYGOHJOJ.js.map +0 -1
- package/dist/github-3WMSTNTK.js +0 -32
- package/dist/github-OXSJQ4PS.js +0 -27
- package/dist/lint-YDHVVZHT.js +0 -30
- package/dist/list-3ZO2NIYO.js +0 -31
- package/dist/list-EJH6VAOW.js +0 -29
- package/dist/run-IP6NZHAG.js +0 -33
- package/dist/validate-G6HH3YXL.js +0 -16
- /package/dist/{batch-GOE3UXOD.js.map → batch-U6S2JUUN.js.map} +0 -0
- /package/dist/{build-M7BMEHL2.js.map → build-4RN6WBCP.js.map} +0 -0
- /package/dist/{calibrate-6RRAILUY.js.map → calibrate-6VIZYR4L.js.map} +0 -0
- /package/dist/{capture-RJPQ6B4N.js.map → capture-DP3NXAZE.js.map} +0 -0
- /package/dist/{check-BQD64X3N.js.map → check-ERVNGKLC.js.map} +0 -0
- /package/dist/{check-ESNS7ZBC.js.map → check-OAJWC3WD.js.map} +0 -0
- /package/dist/{check-RQIIIURI.js.map → check-STHXZIZO.js.map} +0 -0
- /package/dist/{chunk-TA42STP7.js.map → chunk-2ZWTSRE5.js.map} +0 -0
- /package/dist/{chunk-F2OVHJNC.js.map → chunk-4Z2MN5FR.js.map} +0 -0
- /package/dist/{chunk-TCC737IS.js.map → chunk-5VWDIWOH.js.map} +0 -0
- /package/dist/{chunk-ELZ2IIOF.js.map → chunk-6URSCTTX.js.map} +0 -0
- /package/dist/{chunk-K3U5F2QT.js.map → chunk-6VDBP5UV.js.map} +0 -0
- /package/dist/{chunk-C3KI45RF.js.map → chunk-7XZ6INKO.js.map} +0 -0
- /package/dist/{chunk-SNEHX4VE.js.map → chunk-B5CMM57M.js.map} +0 -0
- /package/dist/{chunk-GAZFKZHM.js.map → chunk-CEVU3GFF.js.map} +0 -0
- /package/dist/{chunk-52PDIZKV.js.map → chunk-ECVQNGTH.js.map} +0 -0
- /package/dist/{chunk-NGK3N6F2.js.map → chunk-GCC52YZD.js.map} +0 -0
- /package/dist/{chunk-NN4ASVTY.js.map → chunk-IIFADWWI.js.map} +0 -0
- /package/dist/{chunk-OIZ3XMKB.js.map → chunk-IOL74S4J.js.map} +0 -0
- /package/dist/{chunk-UK2I3ACY.js.map → chunk-IZBBXKMJ.js.map} +0 -0
- /package/dist/{chunk-QY3DOZMS.js.map → chunk-LONRQBGV.js.map} +0 -0
- /package/dist/{chunk-XWFNO5PO.js.map → chunk-MIBWI5H7.js.map} +0 -0
- /package/dist/{chunk-C6HZ6C5T.js.map → chunk-NIFEEIGQ.js.map} +0 -0
- /package/dist/{chunk-JQU7FHYH.js.map → chunk-NZ6TNSCU.js.map} +0 -0
- /package/dist/{chunk-MZC62HPZ.js.map → chunk-PAOVVHQX.js.map} +0 -0
- /package/dist/{chunk-HQFVJDEN.js.map → chunk-QQNPVW3J.js.map} +0 -0
- /package/dist/{chunk-IA7BMIPE.js.map → chunk-UZKQSWHJ.js.map} +0 -0
- /package/dist/{chunk-CP4ZM43V.js.map → chunk-WTEILBKC.js.map} +0 -0
- /package/dist/{chunk-BOIF2XGG.js.map → chunk-XXDXIIQ4.js.map} +0 -0
- /package/dist/{chunk-DX32P2DT.js.map → chunk-ZKAKA54B.js.map} +0 -0
- /package/dist/{chunk-TVIX653Q.js.map → chunk-ZKCNPKUO.js.map} +0 -0
- /package/dist/{chunk-TU2WUBVZ.js.map → cli-YL3IDR52.js.map} +0 -0
- /package/dist/{cli-QKIJOXGW.js.map → cli-ZQUJYQBA.js.map} +0 -0
- /package/dist/{cli-YW6VWBHX.js.map → config-O6UBGEDK.js.map} +0 -0
- /package/dist/{github-3WMSTNTK.js.map → conformance-ALLPNGEQ.js.map} +0 -0
- /package/dist/{github-OXSJQ4PS.js.map → github-UCYLM3DR.js.map} +0 -0
- /package/dist/{json-4DCKDGUV.js.map → github-VEJFO6JJ.js.map} +0 -0
- /package/dist/{lint-YDHVVZHT.js.map → host-6GURH5HE.js.map} +0 -0
- /package/dist/{list-3ZO2NIYO.js.map → json-6F52FSXZ.js.map} +0 -0
- /package/dist/{list-EJH6VAOW.js.map → lint-X2GNINK6.js.map} +0 -0
- /package/dist/{page-QEDB7EVU.js.map → list-DP3O3CWB.js.map} +0 -0
- /package/dist/{pretty-5DGEBZSV.js.map → list-P77RZFZF.js.map} +0 -0
- /package/dist/{pretty-SQX33UM2.js.map → page-WQCMBNRD.js.map} +0 -0
- /package/dist/{reporters-OHSI3RMV.js.map → pretty-5MMMNSWF.js.map} +0 -0
- /package/dist/{reporters-YELDDYJD.js.map → pretty-H4DQREMW.js.map} +0 -0
- /package/dist/{run-IP6NZHAG.js.map → provider-AMGRDY6V.js.map} +0 -0
- /package/dist/{validate-G6HH3YXL.js.map → reporters-SU3VUKKX.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/a11y/cli.ts","../src/a11y/types.ts","../src/a11y/core/url.ts","../src/a11y/core/crawl.ts","../src/a11y/core/exclude.ts","../src/a11y/core/seeds.ts","../src/a11y/core/sitemap.ts","../src/a11y/commands/check.ts","../src/a11y/core/analyzer.ts","../src/a11y/core/config.ts","../src/a11y/reporters/github.ts","../src/a11y/reporters/json.ts","../src/a11y/reporters/pretty.ts","../src/a11y/reporters/index.ts","../src/a11y/reporters/progress.ts"],"sourcesContent":["/**\n * The a11y tool's CLI. A thin commander wrapper over `runCheck`, following\n * clig.dev the way meta's does: primary output to stdout, diagnostics to\n * stderr, color only on a TTY (never under NO_COLOR/--no-color), and the\n * exit codes 0 ok, 1 findings, 2 operational/usage.\n *\n * Every flag is validated here and turned into an `A11yError` before the\n * browser is asked to launch, so a typo costs a message and not a launch.\n */\nimport { Command } from \"commander\";\nimport pkg from \"../../package.json\" with { type: \"json\" };\nimport { shouldColor } from \"../shared/color.js\";\nimport { FAMILY_CONFIG_NAMES } from \"../shared/config-file.js\";\nimport { fail } from \"../shared/run.js\";\nimport { CHECK_DEFAULTS, runCheck } from \"./commands/check.js\";\nimport { createPlaywrightAnalyzer } from \"./core/analyzer.js\";\nimport { excludeEntryLabel, loadA11yConfig, type LoadedA11yConfig } from \"./core/config.js\";\nimport { MUST_START_WITH_SLASH, isUrlPathGlob, type ExcludeGlob } from \"./core/exclude.js\";\nimport { resolveSeeds } from \"./core/seeds.js\";\nimport { A11Y_FORMAT_LIST, isA11yFormat, render } from \"./reporters/index.js\";\nimport { CLEAR_LINE, createProgressReporter } from \"./reporters/progress.js\";\nimport {\n A11yError,\n SEVERITY_LIST,\n isSeverity,\n type Severity,\n type ProgressListener,\n} from \"./types.js\";\n\n/**\n * `--collection <name>` and `--exclude <glob>`: one value per occurrence,\n * never split on commas. The two lines are meta's `collect`, copied rather\n * than imported: a tool does not reach into a sibling tool's CLI.\n */\nfunction collect(value: string, prev: string[]): string[] {\n return prev.concat([value]);\n}\n\n/** `--tags <list>`: commas separate, whitespace around them is trimmed, empty items are dropped. */\nfunction splitList(value: string): string[] {\n return value\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\n/**\n * Whether to paint what goes to `stream`. The report and the progress line\n * are decided separately, since stdout may be a pipe while stderr is still\n * the terminal (`-f json | jq`), and the other way round.\n */\nfunction resolveColor(program: Command, stream: NodeJS.WriteStream): boolean {\n // commander maps --no-color to opts.color === false.\n const noColor = program.opts().color === false;\n // `isTTY` is undefined off a terminal, never false; `shouldColor` treats a\n // missing one as \"not a terminal\".\n return shouldColor({ noColor, isTTY: stream.isTTY });\n}\n\ninterface Progress {\n /** Hears the run's events; `undefined` keeps the run silent. */\n listener: ProgressListener | undefined;\n /**\n * Erase the status line. On a terminal it is rewritten in place, and a run\n * that dies mid-crawl (no browser, a seed that will not load) would leave\n * it there for the error to land on. A no-op everywhere else.\n */\n clear: () => void;\n}\n\n/**\n * `--progress` and `--no-progress` decide; with neither, progress is on\n * exactly when stderr is a terminal, so a CI log stays as it was.\n */\nfunction resolveProgress(program: Command, flag: boolean | undefined): Progress {\n // @types/node says boolean; off a terminal it is undefined (see resolveColor).\n const tty = (process.stderr.isTTY as boolean | undefined) ?? false;\n const silent: Progress = { listener: undefined, clear: () => undefined };\n if (!(flag ?? tty)) return silent;\n const listener = createProgressReporter({\n stream: process.stderr,\n color: resolveColor(program, process.stderr),\n tty,\n });\n if (!tty) return { listener, clear: silent.clear };\n return {\n listener,\n clear: () => {\n process.stderr.write(CLEAR_LINE);\n },\n };\n}\n\n/**\n * A positive integer flag, in canonical form. Anything else is a usage error:\n * `0`, `-3`, `2.5`, `many`, and `01`, since a leading zero is not how anyone\n * writes a page count and would read as an octal literal elsewhere.\n * Exported for its unit test only.\n */\nexport function positiveInteger(value: string, flag: string): number {\n if (!/^[1-9]\\d*$/.test(value)) {\n throw new A11yError(`${flag} must be an integer >= 1.`);\n }\n return Number(value);\n}\n\n/**\n * `--max-pages <n>` and `--no-max-pages` share one option value, the way `-c`\n * and `--no-config` do, so the pair has three states rather than two:\n * a number the user typed, `\"uncapped\"` for the negation, and `undefined`\n * when neither half was typed and the config key still decides.\n */\nexport type MaxPagesFlag = number | \"uncapped\" | undefined;\n\n/**\n * The typed half of that pair, in canonical form. commander hands the\n * negation over as `false`, which is not the same as saying nothing: it is\n * the typed way to ask for a run with no cap at all. Parsed before the config\n * file is read, so a bad value costs a message and nothing else.\n * Exported for its unit test only.\n */\nexport function parseMaxPages(value: string | false | undefined): MaxPagesFlag {\n if (value === false) return \"uncapped\";\n if (value === undefined) return undefined;\n return positiveInteger(value, \"--max-pages\");\n}\n\n/**\n * The cap in force: a typed flag beats the config key, and `undefined` out\n * means no cap, which is what `runCheck` reads as \"check everything found\".\n * Exported for its unit test only.\n */\nexport function resolveMaxPages(\n flag: MaxPagesFlag,\n configured: number | undefined,\n): number | undefined {\n if (flag === \"uncapped\") return undefined;\n return flag ?? configured;\n}\n\n/**\n * Whether to crawl: `--crawl` and `--no-crawl` are both declared, so\n * commander holds no default for the pair and `undefined` means the user\n * typed neither. Same three-way precedence as meta: CLI flag > config key >\n * built-in default.\n * Exported for its unit test only.\n */\nexport function resolveCrawl(flag: boolean | undefined, configured: boolean | undefined): boolean {\n return flag ?? configured ?? CHECK_DEFAULTS.crawl;\n}\n\n/**\n * Every `--exclude` value has to be able to match a path, which always starts\n * with `/`. Checked before the config file is read and long before a browser\n * launches, so a pattern that could only ever miss costs a message. Each\n * survivor carries the flag spelling a later message names it by.\n * Exported for its unit test only.\n */\nexport function assertExcludeGlobs(globs: readonly string[]): ExcludeGlob[] {\n return globs.map((glob) => {\n if (!isUrlPathGlob(glob)) {\n throw new A11yError(`--exclude \"${glob}\" ${MUST_START_WITH_SLASH}`);\n }\n return { glob, source: `--exclude \"${glob}\"` };\n });\n}\n\n/**\n * The same list as it arrives from `a11y.exclude:`, named the way the config\n * errors name it. A run whose patterns came from a file never reports them as\n * a flag, which would send a reader looking through a shell history for\n * something they never typed.\n * Exported for its unit test only.\n */\nexport function configExcludeGlobs(\n globs: readonly string[],\n source: string | null,\n): ExcludeGlob[] {\n // The CLI reaches here only with a file behind it, since `cfg.exclude` is\n // set, so the fallback is for a programmatic caller that passed a section\n // with no source. It names the file a reader would look for rather than\n // labelling an entry with \"null\".\n const file = source ?? FAMILY_CONFIG_NAMES[0] ?? \"manni.config.yaml\";\n return globs.map((glob, i) => ({ glob, source: excludeEntryLabel(file, i) }));\n}\n\nfunction assertSeverity(value: string): Severity {\n if (!isSeverity(value)) {\n throw new A11yError(`Unknown --severity \"${value}\". Use ${SEVERITY_LIST}.`);\n }\n return value;\n}\n\ninterface CheckCliOptions {\n /** `-f, --format` (default \"pretty\"). */\n format: string;\n /** `--crawl` → true, `--no-crawl` → false, neither → undefined (config decides). */\n crawl?: boolean;\n /** `--max-pages <n>` → the string; `--no-max-pages` → commander's false; neither → undefined. */\n maxPages?: string | false;\n /** `--tags <list>`, split on \",\" here (same helper shape as meta's --ext). */\n tags?: string;\n /** `--severity <level>`. */\n severity: string;\n /** `--timeout <ms>`, parsed to int here. */\n timeout: string;\n /** `-q, --quiet`. */\n quiet?: boolean;\n /** `--progress` → true, `--no-progress` → false, neither → undefined (auto). */\n progress?: boolean;\n /** `--collection <name>`, one value per occurrence. `[]` when never given. */\n collection: string[];\n /** `--exclude <glob>`, one value per occurrence. `[]` when never given. */\n exclude: string[];\n /** `-c <path>` | `--no-config` → false. */\n config?: string | false;\n}\n\n/** Commander program named \"a11y\"; the umbrella mounts it. No entry point. */\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name(\"a11y\")\n .description(\"Crawl a site and check every page for accessibility violations with axe-core\")\n .version(pkg.version, \"-V, --version\")\n .option(\"--no-color\", \"disable colored output\")\n // A pointer, not the whole help screen; the message before it already\n // names the offending flag.\n .showHelpAfterError(\"(add --help for usage)\")\n // MUST come before the `.command()` call below. `copyInheritedSettings`\n // copies `_exitCallback` **by value** at subcommand-creation time, so an\n // `exitOverride()` installed afterwards leaves the subcommand calling\n // `process.exit(1)` while the program-level case looks fixed.\n .exitOverride();\n\n program\n .command(\"check\")\n .description(\"Crawl from the given URLs and check every page with axe-core\")\n .argument(\n \"[urls...]\",\n \"http(s) seed URLs; falls back to a collection's url: or a11y.urls in manni.config.yaml\",\n )\n .option(\"-f, --format <format>\", `output: ${A11Y_FORMAT_LIST}`, \"pretty\")\n // `--crawl` first, so that `--no-crawl` leaves the pair's default\n // undefined instead of commander's `true` for a lone negation. Undefined\n // is what lets `crawl:` in config be read as the next rung down.\n .option(\"--crawl\", \"crawl from the given URLs: sitemap and link following (default)\")\n .option(\"--no-crawl\", \"check exactly the given URLs: no sitemap, no link following\")\n // No commander default on purpose: the built-in behaviour is to check\n // everything discovered, and \"no cap\" is not a value commander could hold.\n .option(\n \"--max-pages <n>\",\n \"cap on pages checked; the rest are reported as skipped (default: no cap)\",\n )\n .option(\"--no-max-pages\", \"check every page found, ignoring a configured maxPages\")\n .option(\n \"--exclude <glob>\",\n \"URL path glob to keep out of the crawl; repeatable\",\n collect,\n [],\n )\n .option(\"--tags <list>\", \"comma-separated axe tags to restrict the rules to\")\n .option(\n \"--severity <level>\",\n `minimum severity reported: ${SEVERITY_LIST}`,\n CHECK_DEFAULTS.severity,\n )\n .option(\n \"--timeout <ms>\",\n \"per-page navigation timeout in milliseconds\",\n String(CHECK_DEFAULTS.timeout),\n )\n .option(\"-q, --quiet\", \"in pretty output, hide pages with no violations\")\n // `--progress` first, so that adding `--no-progress` leaves the default\n // undefined (auto) instead of commander's `true` for a lone negation.\n .option(\"--progress\", \"report progress on stderr (default: only on a terminal)\")\n .option(\"--no-progress\", \"never report progress\")\n .option(\n \"--collection <name>\",\n \"configured collection to run over; repeatable\",\n collect,\n [],\n )\n .option(\"-c, --config <path>\", \"path to a manni config file\")\n .option(\"--no-config\", \"ignore any discovered config file\")\n .action(async (urls: string[], options: CheckCliOptions, command: Command) => {\n // Settled once the flags are validated; until then there is no line to clear.\n let clearProgress = (): void => undefined;\n try {\n // A value commander filled in from the option's default yields to the\n // config file; one the user typed wins over it. Same three-way\n // precedence as meta: CLI flag > config key > default.\n const typed = (name: string): boolean =>\n command.getOptionValueSource(name) === \"cli\";\n\n const format = options.format;\n if (!isA11yFormat(format)) {\n throw new A11yError(`Unknown --format \"${format}\". Use ${A11Y_FORMAT_LIST}.`);\n }\n const severity = assertSeverity(options.severity);\n const maxPages = parseMaxPages(options.maxPages);\n const timeout = positiveInteger(options.timeout, \"--timeout\");\n const exclude = assertExcludeGlobs(options.exclude);\n\n const loaded: LoadedA11yConfig =\n options.config === false\n // --no-config drops the collections with the section: the file\n // the user told the run to ignore is where both live.\n ? { config: {}, source: null, collections: [] }\n : await loadA11yConfig(process.cwd(), options.config);\n const cfg = loaded.config;\n\n // Before the analyzer exists: a run with nothing to check, or a\n // --collection that cannot be honoured, costs a message and not a\n // browser launch.\n const seeds = resolveSeeds({\n urls,\n collections: options.collection,\n ...(cfg.urls === undefined ? {} : { configUrls: cfg.urls }),\n declared: loaded.collections,\n source: loaded.source,\n });\n\n const parent = command.parent ?? command;\n const progress = resolveProgress(parent, options.progress);\n clearProgress = progress.clear;\n\n const run = await runCheck(\n {\n urls: seeds,\n crawl: resolveCrawl(options.crawl, cfg.crawl),\n // Typed flag > config key > no cap. There is no default to fall\n // to, and `--no-max-pages` is the typed way to ask for none.\n maxPages: resolveMaxPages(maxPages, cfg.maxPages),\n tags:\n options.tags !== undefined\n ? splitList(options.tags)\n : (cfg.tags ?? CHECK_DEFAULTS.tags),\n // A typed flag replaces the config list entirely; the two do not\n // merge. Merging would only ever let a run exclude *more* than\n // the repository's default, so checking one excluded section on\n // purpose would need --no-config, which also drops collections:.\n exclude: typed(\"exclude\")\n ? exclude\n : configExcludeGlobs(cfg.exclude ?? [], loaded.source),\n severity: typed(\"severity\") ? severity : (cfg.severity ?? CHECK_DEFAULTS.severity),\n timeout: typed(\"timeout\") ? timeout : (cfg.timeout ?? CHECK_DEFAULTS.timeout),\n },\n { analyzer: createPlaywrightAnalyzer(), onProgress: progress.listener },\n );\n\n const color = resolveColor(parent, process.stdout);\n const text = render(format, run, { color, quiet: Boolean(options.quiet) });\n // Only `github` may say nothing on a clean run; every other format\n // owes its envelope even when it is empty.\n if (text.length > 0 || format !== \"github\") {\n process.stdout.write(`${text}\\n`);\n }\n process.exitCode = run.summary.failed > 0 ? 1 : 0;\n } catch (err) {\n clearProgress();\n fail(err);\n }\n });\n\n return program;\n}\n\n","/**\n * The shapes the a11y tool produces and the error it throws.\n *\n * Everything downstream of the browser — the crawl, the check core, the\n * reporters — speaks in these types, so the analyzer is the only module that\n * knows axe's own result shape.\n */\nimport { ToolError } from \"../shared/errors.js\";\nimport type { Severity } from \"../shared/severity.js\";\n\n/**\n * The severity scale is the family's, not a11y's. It is re-exported here so\n * the tool's own modules and tests have one import for everything they speak.\n */\nexport {\n SEVERITIES,\n SEVERITY_LIST,\n isSeverity,\n meetsSeverity,\n type Severity,\n} from \"../shared/severity.js\";\n\n/** Operational/usage failure of the a11y tool: one line on stderr, exit 2. */\nexport class A11yError extends ToolError {\n constructor(message: string) {\n super(message);\n this.name = \"A11yError\";\n }\n}\n\n/**\n * axe's own scale, which it calls impact. Least to most severe. The analyzer\n * maps these onto the family `Severity` (`severityOf`) and keeps the original\n * on the violation as `impact`.\n */\nexport const AXE_IMPACTS = [\"minor\", \"moderate\", \"serious\", \"critical\"] as const;\nexport type AxeImpact = (typeof AXE_IMPACTS)[number];\n\nexport function isAxeImpact(value: string): value is AxeImpact {\n return (AXE_IMPACTS as readonly string[]).includes(value);\n}\n\nexport interface ViolationNode {\n /** axe's CSS selector path to the element (joined `target`). */\n target: string;\n /** The element's outer HTML, as axe reports it (truncated to 200 chars in pretty output only). */\n html: string;\n /** axe's failureSummary: what to fix, in prose. */\n summary: string;\n}\n\nexport interface Violation {\n /** axe rule id, e.g. \"image-alt\". */\n id: string;\n /** The family level, mapped from `impact`. What the floor and the reporters use. */\n severity: Severity;\n /**\n * axe's own value, kept for lookup against Deque's rule pages and the axe\n * DevTools report a team already reads. An unknown or `null` impact is\n * recorded as `\"minor\"`.\n */\n impact: AxeImpact;\n /** Short rule description, e.g. \"Images must have alternate text\". */\n help: string;\n /** Deque University rule page. */\n helpUrl: string;\n /** axe tags, e.g. [\"cat.text-alternatives\", \"wcag2a\", \"wcag111\"]. */\n tags: string[];\n nodes: ViolationNode[];\n}\n\nexport interface PageResult {\n /** Normalized URL that was checked. */\n url: string;\n /** How the URL entered the run. */\n source: \"seed\" | \"sitemap\" | \"link\";\n /** Violations at or above the severity floor. Empty when `error` is set. */\n violations: Violation[];\n /** Count of axe rules that passed on this page. */\n passes: number;\n /** Count of rules axe could not decide (reported, never counted as failures). */\n incomplete: number;\n /**\n * round(100 × passes ÷ (passes + violations.length)), the share of applicable\n * axe rules that passed after severity filtering. Not a Lighthouse score.\n * `null` when the page did not load, or when no rule applied.\n */\n score: number | null;\n /** Set when the page could not be loaded or analyzed; the page counts as failed. */\n error?: string;\n}\n\n/**\n * A page that sent the browser elsewhere once it had loaded, by a meta refresh\n * or a script. It is not analyzed and is not a result: its destination joins\n * the crawl in its place, under the same rules as any other URL.\n */\nexport interface Redirect {\n /** The page that redirected, as queued. */\n url: string;\n /** Where it redirects, normalized. Present whether or not the destination was checked. */\n to: string;\n /** How the redirecting page entered the run. Its destination inherits it. */\n source: PageResult[\"source\"];\n}\n\nexport interface CheckSummary {\n /** Distinct URLs that entered the frontier (seeds + sitemap + links). */\n discovered: number;\n /** Pages actually analyzed. */\n checked: number;\n /** Discovered but not checked because the `maxPages` cap was reached; `0` without a cap. */\n skipped: number;\n /**\n * Discovered, then dropped unloaded because the browser had already landed\n * on the same page under another spelling (a redirect target). Neither\n * checked nor skipped:\n * `checked + skipped + duplicates + redirects === discovered`.\n */\n duplicates: number;\n /**\n * Pages discovered, then not analyzed because they redirect in the browser\n * (a meta refresh or a script). Each one is listed in `CheckRun.redirects`.\n */\n redirects: number;\n /**\n * Distinct URLs an `--exclude` pattern kept out of the crawl, counted once\n * each however many pages linked to them, and keyed the way the frontier\n * dedupes. It sits **outside** the identity above, because an excluded URL\n * never entered the frontier. `0` on a run that excludes nothing.\n */\n excluded: number;\n /**\n * Pages with ≥1 remaining violation, whatever its severity, or an `error`.\n * Drives exit 1. The floor is the gate here, unlike `meta` and `cite`, which\n * fail on error-severity findings alone; proposal 0064.\n */\n failed: number;\n /** Total remaining violations across pages (rules, not nodes). */\n violations: number;\n /** Violations by family severity, always all three keys, zero-filled. */\n bySeverity: Record<Severity, number>;\n /** The sitemap URL that was found, or `null` if none was. Finding one is not using one: see `sitemapPages`. */\n sitemap: string | null;\n /**\n * Same-host, in-scope page URLs the sitemap listed. `0` when no sitemap was\n * found, and `0` when one was found whose every `<loc>` was out of scope — a\n * built sitemap listing production URLs, read on a local preview, is the\n * ordinary way that happens.\n *\n * This is the count the progress line reports, so it is taken before the\n * crawl dedupes against the seeds. A seed that the sitemap also lists is\n * counted here, which means the number can overstate what the sitemap alone\n * contributed to the frontier.\n */\n sitemapPages: number;\n /**\n * Whether the run discovered pages beyond the seeds (sitemap and same-host\n * links). `false` under `--no-crawl`, where `sitemap` is always `null` and\n * no link was followed.\n */\n crawl: boolean;\n}\n\nexport interface CheckRun {\n results: PageResult[];\n /** Pages that redirected in the browser, in visit order. Never in `results`. */\n redirects: Redirect[];\n summary: CheckSummary;\n}\n\n/**\n * What a run says while it is still running. The crawl emits these in order\n * (`browser`, then `page` and `checked` or `redirected` per page, then\n * `done`); the check core adds `sitemap` before them when it crawls. A\n * listener is optional, and nothing is emitted without one.\n */\nexport type ProgressEvent =\n /** About to launch the browser, before the first page. */\n | { kind: \"browser\" }\n /** Discovery finished: the sitemap that was found (or none), and how many in-scope URLs it gave. */\n | { kind: \"sitemap\"; source: string | null; urls: number }\n /**\n * Discovery settled and `--exclude` took `urls` distinct URLs out, across\n * `patterns` patterns. Emitted once, before the browser starts, and only\n * when something was excluded. There is no event per excluded URL: a\n * pattern that removes four hundred pages would bury the ones checked.\n *\n * `urls` is what the seeds and the sitemap gave the run. Links are followed\n * afterwards, and a page excluded that way is counted in `summary.excluded`\n * without reaching this event, so the two can disagree and the summary is\n * the authoritative total.\n */\n | { kind: \"excluded\"; urls: number; patterns: number }\n /** About to check page `index` (1-based); `queued` is every URL discovered so far. */\n | { kind: \"page\"; index: number; queued: number; url: string }\n /**\n * Page `index` finished. `violations` is axe's count for the page before the\n * severity floor is applied, so it can be higher than what the report keeps.\n * `error` is set when the page could not be loaded or analyzed. A seed that\n * fails ends the run instead, and is reported as the run's error.\n */\n | { kind: \"checked\"; index: number; url: string; violations: number; error?: string }\n /**\n * Page `index` sent the browser to `to` once loaded, so it was not\n * analyzed. Emitted in place of `checked`; the counter counts checked\n * pages, so the next page reuses `index`.\n */\n | { kind: \"redirected\"; index: number; url: string; to: string }\n /** The crawl is over: pages analyzed, and pages left in the frontier. */\n | { kind: \"done\"; checked: number; skipped: number };\n\nexport type ProgressListener = (event: ProgressEvent) => void;\n","/**\n * URL helpers for the crawl.\n *\n * Three questions, asked of every candidate before it enters the frontier:\n * what is its canonical spelling (so `/a#top` and `/a` are one page), is it\n * on a host the run is allowed to visit, and is it a page at all rather than\n * a stylesheet or a PDF. The WHATWG parser does most of the first one.\n */\nimport { A11yError } from \"../types.js\";\n\n/**\n * Canonical spelling for dedupe: parse with `new URL`, drop the fragment,\n * lowercase the hostname, drop a default port, keep path/query/trailing slash\n * exactly as given. Throws `A11yError` for an unparseable string.\n */\nexport function normalizeUrl(url: string): string {\n const parsed = parse(url);\n if (parsed === null) throw new A11yError(`Not a valid URL: \"${url}\".`);\n parsed.hash = \"\";\n return parsed.href;\n}\n\n/**\n * The key two spellings of one page share: `normalizeUrl`, then one trailing\n * `/` dropped from the path unless the path is exactly `/`. The query stays.\n * `/a` and `/a/` can be different resources, but on a static site they are one\n * page, and a crawl that checks both double-counts it. Only the seen set uses\n * this; the URL fetched and reported keeps the spelling it was given. Throws\n * `A11yError` for an unparseable string.\n */\nexport function dedupeKey(url: string): string {\n const parsed = new URL(normalizeUrl(url));\n if (parsed.pathname.length > 1 && parsed.pathname.endsWith(\"/\")) {\n parsed.pathname = parsed.pathname.slice(0, -1);\n }\n return parsed.href;\n}\n\n/** `http:` or `https:` and parseable. */\nexport function isHttpUrl(url: string): boolean {\n const parsed = parse(url);\n return parsed !== null && isHttpProtocol(parsed);\n}\n\n/**\n * Same site = same hostname (case-insensitive) and same non-default port.\n * `http://` and `https://` of one host are the same site; `www.example.com`\n * and `example.com` are not.\n */\nexport function sameHost(a: string, b: string): boolean {\n const left = parse(a);\n const right = parse(b);\n if (left === null || right === null) return false;\n // The parser has already dropped `:80` on http and `:443` on https, so an\n // empty port on both sides means \"the scheme's default\" and the two schemes\n // of one host compare equal, as the doc comment promises. Only an explicit\n // non-default port survives to differ.\n return (\n left.hostname.toLowerCase() === right.hostname.toLowerCase() &&\n left.port === right.port\n );\n}\n\n/**\n * A link worth loading as a page: http(s), not `mailto:`/`tel:`/`javascript:`/\n * `data:`, and the last path segment does not end in one of ASSET_EXTENSIONS.\n */\nexport function isPageLink(url: string): boolean {\n const parsed = parse(url);\n if (parsed === null || !isHttpProtocol(parsed)) return false;\n const last = parsed.pathname.slice(parsed.pathname.lastIndexOf(\"/\") + 1);\n const dot = last.lastIndexOf(\".\");\n if (dot === -1) return true;\n return !ASSET_EXTENSIONS.has(last.slice(dot).toLowerCase());\n}\n\nexport const ASSET_EXTENSIONS: ReadonlySet<string> = new Set([\n \".pdf\",\n \".zip\",\n \".gz\",\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".svg\",\n \".webp\",\n \".avif\",\n \".ico\",\n \".css\",\n \".js\",\n \".mjs\",\n \".map\",\n \".json\",\n \".xml\",\n \".txt\",\n \".mp3\",\n \".mp4\",\n \".webm\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n]);\n\nfunction parse(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n}\n\nfunction isHttpProtocol(url: URL): boolean {\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n}\n","/**\n * The crawl: which pages a run visits, and in what order.\n *\n * A breadth-first walk over a FIFO frontier. Seeds go in first, then the\n * sitemap's pages, then every same-host link each visited page lists, in the\n * order it listed them. A page enters the frontier once, under its `dedupeKey`\n * (so `/a` and `/a/` are one page), keeping the spelling that arrived first.\n * Pages run one at a time so a site sees one request at a time.\n *\n * A page that redirects in the browser is not a page the run checks. Its\n * destination joins the frontier in its place, under the same rules.\n */\nimport type { AnalyzeOptions, PageAnalyzer } from \"./analyzer.js\";\nimport { A11yError, type PageResult, type ProgressListener, type Redirect } from \"../types.js\";\nimport type { ExcludeFilter, ExcludeGlob } from \"./exclude.js\";\nimport { dedupeKey, isHttpUrl, isPageLink, normalizeUrl, sameHost } from \"./url.js\";\n\nexport interface CrawlOptions {\n /** Normalized http(s) seeds; each seed's host is in scope. */\n seeds: string[];\n /** `false` = check exactly `seeds`. */\n crawl: boolean;\n /** Hard cap on pages analyzed. Absent means no cap: the whole frontier is analyzed. */\n maxPages?: number;\n /** Extra URLs to enqueue right after the seeds (from the sitemap). */\n extra: string[];\n /**\n * Keeps matching URLs out of the frontier, beside the same-host and\n * asset-extension checks. Absent means nothing is excluded.\n */\n exclude?: ExcludeFilter;\n analyze: AnalyzeOptions;\n /** Told about each step as it happens. Absent means silent. */\n onProgress?: ProgressListener;\n}\n\nexport interface CrawlOutcome {\n /** In visit order. `source` is \"seed\", \"sitemap\" (came from `extra`) or \"link\". */\n pages: Omit<PageResult, \"score\">[];\n /** Distinct pages (by `dedupeKey`) that entered the frontier. */\n discovered: number;\n /** Left in the frontier when `maxPages` stopped the run; `0` without a cap. */\n skipped: number;\n /**\n * Dequeued and dropped unloaded, because the browser had already landed on\n * that page under another spelling.\n * `checked + skipped + duplicates + redirects.length` is `discovered`.\n */\n duplicates: number;\n /** Pages that redirected in the browser, in visit order. Never in `pages`. */\n redirects: Redirect[];\n /**\n * Distinct URLs (by `dedupeKey`) an `exclude` pattern kept out. Never part\n * of `discovered`: an excluded URL is dropped where an off-host URL and a\n * `.png` link are, before the frontier.\n */\n excluded: number;\n}\n\ntype Source = PageResult[\"source\"];\n\n/** What `enqueue` did with a URL, and the pattern when one excluded it. */\ntype Enqueued =\n | { as: \"queued\" | \"seen\" | \"invalid\" | \"asset\" | \"off-host\" }\n | { as: \"excluded\"; pattern: ExcludeGlob };\n\ninterface Candidate {\n url: string;\n key: string;\n source: Source;\n}\n\n/**\n * BFS over a FIFO frontier: seeds, then `extra`, then links in the order each\n * page listed them. Every candidate goes through `normalizeUrl`, and is\n * enqueued once per `dedupeKey` (so a trailing slash does not make a second\n * page), only when `isPageLink` and `sameHost` with **any** seed. The first\n * spelling seen is the one fetched and reported. Pages run sequentially.\n * - A seed that fails to analyze rethrows (nothing was checked: exit 2).\n * - Any other page's failure is recorded as `error` and the crawl continues.\n * - A page's `finalUrl` (where the browser landed) counts as visited, so a\n * queued spelling of it is dropped at dequeue time (`duplicates`), and a\n * link to it seen afterwards is never queued.\n * - With `maxPages` set, stops when `pages.length === maxPages`; `skipped` =\n * frontier left behind. Without it, runs the frontier dry and `skipped` is 0.\n * - With `crawl: false`, `extra` is ignored and no links are followed.\n * - With `exclude` set, a URL whose path a pattern matches is never enqueued,\n * never fetched and never counted in `discovered`; `excluded` counts those,\n * once per key. `onProgress` hears one `excluded` event before `browser`\n * when the count is not zero.\n * - A page that redirects in the browser is recorded in `redirects`, not in\n * `pages`, and does not count toward `maxPages`. Its destination is\n * enqueued with the page's own `source`, under `crawl: false` too, since it\n * is the same page under a new address. A seed whose destination is not\n * http(s), is on another host, or is excluded, rethrows: otherwise nothing\n * would be checked.\n * - `onProgress` hears `browser` once before the first analyze (that is where\n * the lazy launch happens), `page` before each analyze, `checked` (or\n * `redirected`) after each, and `done` at the end. A failing seed rethrows\n * before `checked`.\n */\nexport async function crawl(opts: CrawlOptions, analyzer: PageAnalyzer): Promise<CrawlOutcome> {\n const frontier: Candidate[] = [];\n /** Keys that entered the frontier, or that the browser landed on. */\n const seen = new Set<string>();\n /** Keys analyzed, or that the browser landed on. */\n const visited = new Set<string>();\n /** Keys an `exclude` pattern kept out, so a page linked thirty times counts once. */\n const excluded = new Set<string>();\n const progress = opts.onProgress ?? (() => undefined);\n\n const enqueue = (raw: string, source: Source): Enqueued => {\n let url: string;\n let key: string;\n try {\n url = normalizeUrl(raw);\n key = dedupeKey(url);\n } catch {\n return { as: \"invalid\" };\n }\n // A seed is what the user asked for, so it is never filtered as an asset;\n // it still has to be on its own host, which it is by definition.\n if (source !== \"seed\" && !isPageLink(url)) return { as: \"asset\" };\n if (!opts.seeds.some((seed) => sameHost(seed, url))) return { as: \"off-host\" };\n // After the scope checks, so an off-host or asset URL is not also counted\n // as excluded; the two filters answer different questions and a URL that\n // was never in scope was not kept out by a pattern.\n const pattern = opts.exclude?.match(url) ?? null;\n if (pattern !== null) {\n excluded.add(key);\n return { as: \"excluded\", pattern };\n }\n if (seen.has(key)) return { as: \"seen\" };\n seen.add(key);\n frontier.push({ url, key, source });\n return { as: \"queued\" };\n };\n\n /** Where the browser landed is a page the run has now checked, whatever it was asked for. */\n const landed = (finalUrl: string | undefined): void => {\n if (finalUrl === undefined) return;\n try {\n const key = dedupeKey(finalUrl);\n seen.add(key);\n visited.add(key);\n } catch {\n // Not a URL the crawl could ever queue, so nothing to mark.\n }\n };\n\n for (const seed of opts.seeds) enqueue(seed, \"seed\");\n if (opts.crawl) for (const url of opts.extra) enqueue(url, \"sitemap\");\n // Discovery has settled as far as it can before a page is fetched, and this\n // is where a pattern that matched far more than intended is worth seeing:\n // the browser has not launched yet, so the run is still cheap to stop.\n if (excluded.size > 0) {\n progress({ kind: \"excluded\", urls: excluded.size, patterns: opts.exclude?.patterns.length ?? 0 });\n }\n\n const pages: CrawlOutcome[\"pages\"] = [];\n const redirects: Redirect[] = [];\n let duplicates = 0;\n const capped = (): boolean => opts.maxPages !== undefined && pages.length >= opts.maxPages;\n for (let next = 0; next < frontier.length && !capped(); next++) {\n const candidate = frontier[next];\n if (candidate === undefined) break;\n const { url, key, source } = candidate;\n if (visited.has(key)) {\n duplicates += 1;\n continue;\n }\n visited.add(key);\n const index = pages.length + 1;\n // Not `index === 1`: a redirect pushes no page, so the next one reuses that index.\n if (pages.length + redirects.length === 0) progress({ kind: \"browser\" });\n progress({ kind: \"page\", index, queued: frontier.length, url });\n try {\n const analyzed = await analyzer.analyze(url, opts.analyze);\n if (\"redirect\" in analyzed) {\n const to = normalized(analyzed.redirect);\n // Recorded and narrated before the seed checks below on purpose: the\n // `↪` line then precedes the fatal message that explains it, and a\n // throw discards `redirects` anyway.\n redirects.push({ url, to, source });\n progress({ kind: \"redirected\", index, url, to });\n const outcome = enqueue(to, source);\n if (source === \"seed\") {\n if (!isHttpUrl(to)) {\n throw new A11yError(`${url} redirects to ${to}, which is not an http(s) URL.`);\n }\n if (outcome.as === \"off-host\") {\n throw new A11yError(`${url} redirects to ${to}, which is on another host. Check ${to} instead.`);\n }\n if (outcome.as === \"excluded\") {\n throw new A11yError(`${outcome.pattern.source} excludes ${to}, where the seed ${url} redirects.`);\n }\n }\n continue;\n }\n pages.push({ ...analyzed.result, url, source });\n landed(analyzed.finalUrl);\n if (opts.crawl) for (const link of analyzed.links) enqueue(link, \"link\");\n progress({ kind: \"checked\", index, url, violations: analyzed.result.violations.length });\n } catch (err) {\n if (source === \"seed\") throw err;\n const error = err instanceof Error ? err.message : String(err);\n pages.push({ url, source, violations: [], passes: 0, incomplete: 0, error });\n progress({ kind: \"checked\", index, url, violations: 0, error });\n }\n }\n\n const skipped = frontier.length - pages.length - duplicates - redirects.length;\n progress({ kind: \"done\", checked: pages.length, skipped });\n return {\n pages,\n discovered: frontier.length,\n skipped,\n duplicates,\n redirects,\n excluded: excluded.size,\n };\n}\n\n/** `normalizeUrl`, or the string as it came when it is not a URL. */\nfunction normalized(url: string): string {\n try {\n return normalizeUrl(url);\n } catch {\n return url;\n }\n}\n","/**\n * Keeping part of a site out of the crawl (proposal 0059).\n *\n * A pattern is matched against the URL's **path** alone, never the scheme,\n * host, port, query or fragment. The host is exactly the part that changes\n * between a local preview and production, so one pattern has to survive the\n * move: `/proposals/**` reads the same against `http://127.0.0.1:4321/` and\n * against `https://example.com/`.\n *\n * A trailing slash is not part of the comparison, because a crawl produces\n * both spellings — a sitemap usually lists `/proposals/`, a bare link usually\n * points at `/proposals`. Stripping it means a pattern cannot catch one and\n * silently miss the other. Stripping it is also what makes `/proposals/**`\n * cover the section index as well as the pages under it, since picomatch's\n * `**` already matches zero segments.\n *\n * Matching itself is the family's file-glob matcher, so an exclusion and a\n * collection's `paths:` read a `*`, a `**` and a dot segment the same way. A\n * URL path never carries a raw backslash — the WHATWG parser rewrites one and\n * percent-encodes the rest — so that matcher's separator normalization cannot\n * change what a pattern here means.\n */\nimport { matchesFileGlob } from \"../../shared/globs.js\";\n\n/**\n * One pattern, and the name a message gives it.\n *\n * A pattern reaches a run from one of two places, and an error about it has to\n * say which. `--exclude \"/proposals/**\"` for a typed flag, and the config\n * file's own shape for a key, so that a run whose patterns came from\n * `a11y.exclude:` never names a flag the user did not type.\n */\nexport interface ExcludeGlob {\n glob: string;\n /** How a message names this pattern: a flag, or a file and a key. */\n source: string;\n}\n\nexport interface ExcludeFilter {\n /** The patterns, in the order they were given. */\n readonly patterns: readonly ExcludeGlob[];\n /** The first pattern that keeps `url` out of the crawl, or `null`. */\n match(url: string): ExcludeGlob | null;\n /** Whether any pattern keeps `url` out of the crawl. */\n matches(url: string): boolean;\n}\n\n/**\n * The tail every \"that is not a path glob\" message ends with, so the flag and\n * the config key say the same thing about the same mistake.\n */\nexport const MUST_START_WITH_SLASH = 'must start with \"/\": it matches a URL path.';\n\n/**\n * Can this pattern ever match a path? A path always starts with `/`, so a\n * pattern that does not is a silent near-miss rather than a filter, and both\n * the flag and the config key refuse it.\n */\nexport function isUrlPathGlob(pattern: string): boolean {\n return pattern.startsWith(\"/\");\n}\n\n/**\n * The path a pattern is matched against: the URL's `pathname` with one\n * trailing `/` dropped, unless the path is exactly `/`. `null` for a string\n * that is not a URL, which the crawl has already dropped for other reasons.\n */\nexport function excludePath(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n const path = parsed.pathname;\n return path.length > 1 && path.endsWith(\"/\") ? path.slice(0, -1) : path;\n}\n\n/**\n * A filter over `patterns`, in order. An empty list excludes nothing, which is\n * the default and the shape a run with neither the flag nor the config key\n * has. Patterns are taken as given: one value is one pattern, and a comma\n * inside it is a literal comma, per 0034's one-separator rule.\n */\nexport function createExcludeFilter(patterns: readonly ExcludeGlob[]): ExcludeFilter {\n const list = [...patterns];\n const match = (url: string): ExcludeGlob | null => {\n if (list.length === 0) return null;\n const path = excludePath(url);\n if (path === null) return null;\n return list.find((pattern) => matchesFileGlob(path, pattern.glob)) ?? null;\n };\n return {\n patterns: list,\n match,\n matches: (url) => match(url) !== null,\n };\n}\n","/**\n * Where a check starts from.\n *\n * A collection may declare `url:`, the site root its documents are published\n * at (proposal 0041, rule 12), which makes \"check the guides\" something a\n * person can mean. Four sources can therefore name a seed, and this module is\n * the one place that decides between them: first non-empty wins, in the order\n * positional URLs, the collections named by `--collection`, `a11y.urls`, then\n * every declared collection that has a `url`.\n *\n * `a11y.urls` keeps its place ahead of the collections because a site has\n * entry points no documentation collection covers, and because nothing that\n * ships today should have to move.\n *\n * It is separate from the CLI, and pure, because the interesting part is the\n * order and the three refusals, and neither is worth a spawned bin with a\n * browser behind it to test.\n */\nimport { selectCollections, type CollectionConfig } from \"../../shared/collections.js\";\nimport { A11yError } from \"../types.js\";\n\n/**\n * Nothing to check. Also thrown by `runCheck` for a programmatic caller that\n * resolved its own seeds, which is why the text lives here and not there.\n */\nexport const NO_SEEDS_MESSAGE =\n \"No URLs to check. Pass one or more, set url: on a collection, or set a11y.urls in manni.config.yaml.\";\n\n/** The family file's name, for a message about a config that was never found. */\nconst CONFIG_NAME = \"manni.config.yaml\";\n\nexport interface SeedSources {\n /** Positional `[urls...]`, as typed. Validated and normalized by `runCheck`. */\n urls: readonly string[];\n /** `--collection` names, one per occurrence. Empty means none were named. */\n collections: readonly string[];\n /** `a11y.urls`, when the key is set. An empty list counts as unset. */\n configUrls?: readonly string[];\n /** Every collection the family file declares, in declaration order. */\n declared: readonly CollectionConfig[];\n /**\n * The config file as the user would name it, for the unknown-name message.\n * `null` or absent (no file found) names the canonical family file, since\n * that is where the missing `collections:` would go.\n */\n source?: string | null;\n}\n\n/**\n * The seeds a run checks, or an `A11yError` explaining why there are none.\n *\n * 1. `urls` when non-empty.\n * 2. The `url` of each collection named by `collections`, in **declaration**\n * order, repeats and shared urls collapsed. A named collection with no\n * `url` is an error, and so is a name that is not declared.\n * 3. `configUrls` when non-empty, exactly as configured.\n * 4. The `url` of every declared collection that has one, in declaration\n * order, shared urls collapsed. One without a `url` contributes nothing.\n *\n * Naming a url-less collection fails where leaving it out of the default set\n * does not: `--collection blog` asked for something specific and has to be\n * told it cannot be done, while a bare `manni a11y check` gets whatever is\n * publishable.\n */\nexport function resolveSeeds(sources: SeedSources): string[] {\n const { urls, collections: names, declared } = sources;\n\n if (names.length > 0) {\n // The shape of the invocation, before anything about the config: a run\n // that named both a collection and a URL has no reading to pick between.\n if (urls.length > 0) {\n throw new A11yError(\n \"--collection selects a configured collection; it cannot be combined with URLs.\",\n );\n }\n // No config at all — nothing was found, or `--no-config` refused it. The\n // flag selects from a config file, so say that rather than reporting the\n // name as unknown: \"Configured: (none).\" blames the name for the absence\n // of the file. `manni meta` refuses the same combination with the same\n // sentence, which is the parity CLAUDE.md asks for on a shared flag. A\n // real config always carries a source, so this cannot mistake a\n // programmatic caller that passed collections without one.\n if (sources.source == null && declared.length === 0) {\n throw new A11yError(\"--collection needs a config file to select from.\");\n }\n const selected = selectCollections(\n declared,\n names,\n sources.source ?? CONFIG_NAME,\n (message) => new A11yError(message),\n );\n const seeds: string[] = [];\n for (const collection of selected) {\n if (collection.url === undefined) {\n throw new A11yError(`collection \"${collection.name}\" has no url: to check.`);\n }\n seeds.push(collection.url);\n }\n return unique(seeds);\n }\n\n if (urls.length > 0) return [...urls];\n\n const configUrls = sources.configUrls ?? [];\n if (configUrls.length > 0) return [...configUrls];\n\n const published = unique(\n declared.flatMap((collection) => (collection.url === undefined ? [] : [collection.url])),\n );\n if (published.length > 0) return published;\n\n throw new A11yError(NO_SEEDS_MESSAGE);\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n","/**\n * Sitemap discovery: the page list a site publishes for itself.\n *\n * A sitemap reaches pages no link points at, so the crawl reads it first and\n * follows links second. Nothing here is allowed to fail the run: a site with\n * no sitemap, a broken one, or one behind a firewall is a site to crawl by\n * links alone, and the outcome says so with `source: null`.\n */\nimport { DOMParser } from \"@xmldom/xmldom\";\nimport { isHttpUrl, isPageLink, normalizeUrl, sameHost } from \"./url.js\";\n\n/** `fetch` narrowed to what this module needs, so tests inject a fake. */\nexport type Fetcher = (\n url: string,\n) => Promise<{ ok: boolean; status: number; text(): Promise<string> }>;\n\nexport interface SitemapDiscovery {\n /** The sitemap (or sitemap index) URL that was read, `null` when none was usable. */\n source: string | null;\n /** Same-host page URLs from every `<loc>`, normalized and deduped, in document order. */\n urls: string[];\n}\n\nexport interface ParsedSitemap {\n kind: \"index\" | \"urlset\";\n locs: string[];\n}\n\n/** How many levels of `<sitemapindex>` nesting are followed below the first. */\nconst MAX_INDEX_DEPTH = 3;\n/** How many child sitemaps one discovery reads, across every index it meets. */\nconst MAX_CHILD_SITEMAPS = 50;\n/** A robots.txt or sitemap that has not answered in this long is \"no sitemap\". */\nconst FETCH_TIMEOUT_MS = 10_000;\n\nconst defaultFetcher: Fetcher = (url) =>\n fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n\n/**\n * The file names tried in each directory of the seed's path, in this order.\n * `sitemap.xml` is the convention; `sitemap-index.xml` is what Astro (and so\n * Starlight) emits; `sitemap_index.xml` is Yoast's and Hugo's spelling.\n */\nconst WELL_KNOWN_SITEMAP_FILES = [\"sitemap.xml\", \"sitemap-index.xml\", \"sitemap_index.xml\"];\n\n/**\n * Candidates, in order:\n * 1. GET `<origin>/robots.txt`; every `Sitemap:` line (case-insensitive key,\n * absolute URL) is a candidate, in order.\n * 2. Then, for each directory of the seed's path from the seed's own\n * directory up to `/`, nearest first (`https://h/manni/meta/fix/` walks\n * `/manni/meta/fix/`, `/manni/meta/`, `/manni/`, `/`; a seed that names a\n * file, `https://h/manni/page.html`, starts at `/manni/`), the files\n * `sitemap.xml`, `sitemap-index.xml`, `sitemap_index.xml`, in that order\n * within each directory. A project site under a path prefix (GitHub Pages)\n * cannot write to the origin's `/robots.txt` or `/sitemap.xml`, which is\n * why the walk starts at the seed and not at the root.\n * Candidates are deduped, keeping the first position of each.\n *\n * Then parse the first candidate that returns 2xx with `@xmldom/xmldom`\n * (`DOMParser`, errors swallowed); a 2xx body that is not a sitemap is skipped\n * and the next candidate tried. A `<sitemapindex>` recurses into each child\n * `<sitemap><loc>` (depth ≤ 3, ≤ 50 child sitemaps). A `<urlset>` yields its\n * `<url><loc>` values. Keep only `sameHost(seed, loc) && isPageLink(loc)`.\n * `source` is the candidate that was actually used.\n *\n * A network error, non-2xx, or unparseable body means \"no sitemap here\" —\n * never an error, and never a page failure. `.xml.gz` bodies are not\n * decompressed (deferred).\n */\nexport async function discoverSitemap(\n seed: string,\n fetcher: Fetcher = defaultFetcher,\n): Promise<SitemapDiscovery> {\n const origin = new URL(seed).origin;\n const candidates = await robotsSitemaps(`${origin}/robots.txt`, fetcher);\n for (const candidate of wellKnownCandidates(seed)) {\n if (!candidates.includes(candidate)) candidates.push(candidate);\n }\n\n for (const candidate of candidates) {\n const parsed = await fetchSitemap(candidate, fetcher);\n if (parsed === null) continue;\n const walk: Walk = { fetcher, seen: new Set([candidate]), childBudget: MAX_CHILD_SITEMAPS };\n const locs = await expand(parsed, 0, walk);\n return { source: candidate, urls: pageUrls(seed, locs) };\n }\n return { source: null, urls: [] };\n}\n\n/** Pure parser, exported for tests: returns `{ kind: \"index\" | \"urlset\", locs: string[] }` or `null`. */\nexport function parseSitemapXml(text: string): ParsedSitemap | null {\n if (text.trim() === \"\") return null;\n let root: XmlNode | null;\n try {\n // Every parse problem is routed to `onError` and ignored; what matters is\n // whether a sitemap root came out the other side.\n const doc = new DOMParser({ onError: () => undefined }).parseFromString(text, \"text/xml\");\n root = doc.documentElement;\n } catch {\n return null;\n }\n if (root === null) return null;\n const kind = root.localName;\n if (kind !== \"sitemapindex\" && kind !== \"urlset\") return null;\n const entry = kind === \"sitemapindex\" ? \"sitemap\" : \"url\";\n const locs: string[] = [];\n for (const child of childElements(root, entry)) {\n for (const loc of childElements(child, \"loc\")) {\n const value = (loc.textContent ?? \"\").trim();\n if (value !== \"\") locs.push(value);\n }\n }\n return { kind: kind === \"sitemapindex\" ? \"index\" : \"urlset\", locs };\n}\n\ntype XmlDocument = ReturnType<DOMParser[\"parseFromString\"]>;\n/**\n * xmldom's `Node`, reached through the parser's own types rather than an\n * import so the alias tracks whatever the installed version declares. It is\n * enough here: `localName`, `textContent` and the sibling walk are all on\n * `Node`, and `documentElement` narrows to it.\n */\ntype XmlNode = NonNullable<XmlDocument[\"firstChild\"]>;\n\n/** Element children of `node` whose local name is `name`, in document order. */\nfunction childElements(node: XmlNode, name: string): XmlNode[] {\n const out: XmlNode[] = [];\n for (let n = node.firstChild; n !== null; n = n.nextSibling) {\n if (n.nodeType === 1 && n.localName === name) out.push(n);\n }\n return out;\n}\n\ninterface Walk {\n fetcher: Fetcher;\n /** Sitemap URLs already read, so an index that lists itself terminates. */\n seen: Set<string>;\n /** Child sitemaps still allowed; shared across the whole recursion. */\n childBudget: number;\n}\n\nasync function expand(parsed: ParsedSitemap, depth: number, walk: Walk): Promise<string[]> {\n if (parsed.kind === \"urlset\") return parsed.locs;\n if (depth >= MAX_INDEX_DEPTH) return [];\n const out: string[] = [];\n for (const child of parsed.locs) {\n if (walk.childBudget <= 0) break;\n if (!isHttpUrl(child) || walk.seen.has(child)) continue;\n walk.seen.add(child);\n walk.childBudget -= 1;\n const nested = await fetchSitemap(child, walk.fetcher);\n if (nested === null) continue;\n out.push(...(await expand(nested, depth + 1, walk)));\n }\n return out;\n}\n\n/**\n * The well-known sitemap file names in every directory of the seed's path,\n * nearest directory first, root last. Query and fragment are dropped; the\n * directory of `/manni/page.html` is `/manni/`, of `/manni/` is itself.\n */\nfunction wellKnownCandidates(seed: string): string[] {\n const url = new URL(seed);\n const directories = ancestorDirectories(url.pathname);\n return directories.flatMap((dir) =>\n WELL_KNOWN_SITEMAP_FILES.map((file) => `${url.origin}${dir}${file}`),\n );\n}\n\n/**\n * `/manni/meta/fix/` → `[\"/manni/meta/fix/\", \"/manni/meta/\", \"/manni/\", \"/\"]`;\n * `/manni/page.html` → `[\"/manni/\", \"/\"]`; `/` → `[\"/\"]`.\n */\nfunction ancestorDirectories(pathname: string): string[] {\n let dir = pathname.slice(0, pathname.lastIndexOf(\"/\") + 1);\n const out: string[] = [];\n for (;;) {\n out.push(dir);\n if (dir === \"/\") return out;\n // Drop the trailing slash, then everything after the previous one.\n const parent = dir.slice(0, dir.lastIndexOf(\"/\", dir.length - 2) + 1);\n dir = parent === \"\" ? \"/\" : parent;\n }\n}\n\n/** The `Sitemap:` candidates from a robots.txt, or none when it is missing. */\nasync function robotsSitemaps(url: string, fetcher: Fetcher): Promise<string[]> {\n const text = await fetchText(url, fetcher);\n if (text === null) return [];\n const out: string[] = [];\n for (const line of text.split(/\\r?\\n/)) {\n const match = /^\\s*sitemap\\s*:\\s*(\\S+)/i.exec(line);\n const candidate = match?.[1];\n if (candidate !== undefined && isHttpUrl(candidate) && !out.includes(candidate)) {\n out.push(candidate);\n }\n }\n return out;\n}\n\nasync function fetchSitemap(url: string, fetcher: Fetcher): Promise<ParsedSitemap | null> {\n const text = await fetchText(url, fetcher);\n return text === null ? null : parseSitemapXml(text);\n}\n\n/** The body of a 2xx response, or `null` for anything else, thrown included. */\nasync function fetchText(url: string, fetcher: Fetcher): Promise<string | null> {\n try {\n const response = await fetcher(url);\n if (!response.ok) return null;\n return await response.text();\n } catch {\n return null;\n }\n}\n\n/** Filter, normalize and dedupe the `<loc>` values into crawlable page URLs. */\nfunction pageUrls(seed: string, locs: string[]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const loc of locs) {\n if (!isHttpUrl(loc) || !sameHost(seed, loc) || !isPageLink(loc)) continue;\n const url = normalizeUrl(loc);\n if (seen.has(url)) continue;\n seen.add(url);\n out.push(url);\n }\n return out;\n}\n","/**\n * `manni a11y check`, the command core.\n *\n * Validates the seeds, finds the sitemap, runs the crawl, drops the findings\n * below the severity floor, scores each page and totals the summary. No CLI\n * and no IO of its own beyond the injected analyzer and fetcher, so it can be\n * tested end to end with neither a browser nor a network.\n */\nimport type { PageAnalyzer } from \"../core/analyzer.js\";\nimport { crawl, type CrawlOutcome } from \"../core/crawl.js\";\nimport { createExcludeFilter, type ExcludeGlob } from \"../core/exclude.js\";\nimport { NO_SEEDS_MESSAGE } from \"../core/seeds.js\";\nimport { discoverSitemap, type Fetcher, type SitemapDiscovery } from \"../core/sitemap.js\";\nimport { isHttpUrl, normalizeUrl } from \"../core/url.js\";\nimport {\n A11yError,\n meetsSeverity,\n type CheckRun,\n type CheckSummary,\n type Severity,\n type PageResult,\n type ProgressListener,\n} from \"../types.js\";\n\nexport interface CheckOptions {\n /** Seeds as typed (validated and normalized here). */\n urls: string[];\n /** Follow the sitemap and same-host links. Default `true`. */\n crawl: boolean;\n /** Stop after this many pages. Absent means no cap: every discovered page is checked. */\n maxPages?: number;\n /** axe tags to restrict to; empty is axe's default rule set. Default `[]`. */\n tags: string[];\n /** Minimum family severity reported and counted. Default `\"notice\"`, everything. */\n severity: Severity;\n /** Per-page navigation timeout in ms. Default `30000`. */\n timeout: number;\n /**\n * Globs matched against a URL's path; a match keeps the page out of the\n * crawl. Default `[]`, which excludes nothing. A pattern that excludes a\n * seed is an error, since the seed and the pattern are both the caller's\n * statement and a contradiction is not this command's to resolve. Each\n * pattern carries the name the refusal gives it, so the message points at\n * the flag or the config key the pattern actually came from.\n */\n exclude: ExcludeGlob[];\n}\n\nexport interface CheckDeps {\n analyzer: PageAnalyzer;\n /** Defaults to global `fetch`. */\n fetcher?: Fetcher;\n /**\n * Told about each step as it happens: `sitemap` once discovery is done\n * (only when crawling), then everything the crawl reports. Absent means\n * silent.\n */\n onProgress?: ProgressListener;\n}\n\n/**\n * `maxPages` is deliberately absent: the default is no cap. A capped crawl\n * reports a partial site as checked, so the cap is opt-in, through\n * `--max-pages` or the config key.\n */\nexport const CHECK_DEFAULTS: Readonly<Omit<CheckOptions, \"urls\">> = Object.freeze({\n crawl: true,\n tags: [],\n severity: \"notice\",\n timeout: 30000,\n exclude: [],\n});\n\n/**\n * 1. `urls.length === 0` → A11yError(NO_SEEDS_MESSAGE). The CLI resolves its\n * seeds with `resolveSeeds` and never arrives here empty; a programmatic\n * caller that passed none hears the same thing.\n * 2. Any non-http(s) url → A11yError(`Not an http(s) URL: \"<url>\".`)\n * 3. Any seed an `exclude` pattern matches → A11yError naming **every** such\n * seed, one per line, each under the pattern's own `source`. A user who\n * typed two wrong patterns should not need one run per fix, and nothing is\n * checked either way.\n * 4. When `crawl`: `discoverSitemap(firstSeed)` (one sitemap per run, the first seed's origin).\n * 5. `crawl(...)`, then per page: drop violations below `severity`, compute `score`.\n * 6. Build `CheckSummary`. Always awaits `analyzer.close()` in `finally`.\n */\nexport async function runCheck(opts: CheckOptions, deps: CheckDeps): Promise<CheckRun> {\n const { analyzer } = deps;\n try {\n if (opts.urls.length === 0) {\n throw new A11yError(NO_SEEDS_MESSAGE);\n }\n for (const url of opts.urls) {\n if (!isHttpUrl(url)) throw new A11yError(`Not an http(s) URL: \"${url}\".`);\n }\n const seeds = opts.urls.map(normalizeUrl);\n\n const exclude = createExcludeFilter(opts.exclude);\n const contradictions = seeds\n .map((seed) => {\n const pattern = exclude.match(seed);\n return pattern === null ? null : `${pattern.source} excludes the seed ${seed}.`;\n })\n .filter((line): line is string => line !== null);\n if (contradictions.length > 0) throw new A11yError(contradictions.join(\"\\n\"));\n\n let sitemap: SitemapDiscovery = { source: null, urls: [] };\n const first = seeds[0];\n if (opts.crawl && first !== undefined) {\n sitemap = await discoverSitemap(first, deps.fetcher);\n deps.onProgress?.({ kind: \"sitemap\", source: sitemap.source, urls: sitemap.urls.length });\n }\n\n const outcome = await crawl(\n {\n seeds,\n crawl: opts.crawl,\n maxPages: opts.maxPages,\n extra: sitemap.urls,\n exclude,\n analyze: { tags: opts.tags, timeout: opts.timeout },\n onProgress: deps.onProgress,\n },\n analyzer,\n );\n\n const results = outcome.pages.map((page) => finishPage(page, opts.severity));\n return {\n results,\n redirects: outcome.redirects,\n summary: summarize(results, outcome, sitemap, opts.crawl),\n };\n } finally {\n await analyzer.close();\n }\n}\n\n/** Apply the severity floor, then score what is left. */\nfunction finishPage(page: Omit<PageResult, \"score\">, floor: Severity): PageResult {\n if (page.error !== undefined) return { ...page, violations: [], score: null };\n const violations = page.violations.filter((v) => meetsSeverity(v.severity, floor));\n const applicable = page.passes + violations.length;\n const score = applicable === 0 ? null : Math.round((100 * page.passes) / applicable);\n return { ...page, violations, score };\n}\n\n/**\n * Does this page fail the run?\n *\n * The level is not read here, and that is the decision rather than an\n * oversight. `--severity` is the floor, and the floor is the gate: anything\n * `finishPage` kept fails the page, whatever its level. `manni meta validate`\n * and `manni cite check` fail on error-severity findings alone, because a\n * user decides there what becomes an error, through the schema or through\n * `cite.severity.<rule>`. axe assigns a11y's severities and nothing in the\n * config moves one, so the floor is the only lever this domain's shape\n * allows. Proposal 0064 records why, and supersedes the one paragraph of\n * 0035 that described the two as separable.\n */\nfunction fails(page: PageResult): boolean {\n return page.error !== undefined || page.violations.length > 0;\n}\n\nfunction summarize(\n results: PageResult[],\n { discovered, skipped, duplicates, redirects, excluded }: CrawlOutcome,\n sitemap: SitemapDiscovery,\n crawl: boolean,\n): CheckSummary {\n // Spelled out so the compiler, not a test, says when the family scale moves.\n const bySeverity: Record<Severity, number> = { notice: 0, warning: 0, error: 0 };\n let failed = 0;\n let violations = 0;\n for (const page of results) {\n if (fails(page)) failed += 1;\n for (const violation of page.violations) {\n violations += 1;\n bySeverity[violation.severity] += 1;\n }\n }\n return {\n discovered,\n checked: results.length,\n skipped,\n duplicates,\n redirects: redirects.length,\n excluded,\n failed,\n violations,\n bySeverity,\n sitemap: sitemap.source,\n // The count the progress line already reported as `(N pages)`, so the\n // report cannot disagree with the narration that preceded it.\n sitemapPages: sitemap.urls.length,\n crawl,\n };\n}\n","/**\n * The browser seam.\n *\n * Everything that needs a real page — navigation, the live DOM's links, axe\n * itself — sits behind `PageAnalyzer`, so the crawl and the check core can be\n * exercised with a canned analyzer and no browser. The Playwright-backed\n * implementation lives here too, behind the same interface.\n */\nimport { AxeBuilder } from \"@axe-core/playwright\";\nimport {\n chromium,\n type Browser,\n type BrowserContext,\n type Page,\n type Request,\n type Response,\n} from \"playwright-core\";\nimport {\n A11yError,\n isAxeImpact,\n type AxeImpact,\n type PageResult,\n type Severity,\n type Violation,\n type ViolationNode,\n} from \"../types.js\";\nimport { dedupeKey } from \"./url.js\";\n\nexport interface AnalyzeOptions {\n /** axe tags to restrict to; empty = axe defaults. */\n tags: string[];\n /** Navigation timeout in ms. */\n timeout: number;\n}\n\nexport interface AnalyzedPage {\n /** Raw result: every violation regardless of severity; `source` and `score` are filled in by `runCheck`. */\n result: Omit<PageResult, \"source\" | \"score\">;\n /** Absolute `href` of every `a[href]` in the live DOM after load, unfiltered. */\n links: string[];\n /**\n * The browser's URL once navigation and any redirects settled. The crawl\n * marks it visited so a later queued spelling of the same page is not\n * loaded again. Absent when the analyzer does not know it.\n */\n finalUrl?: string;\n}\n\n/**\n * A page that sent the browser elsewhere once it had loaded: a meta refresh\n * to another page, or a navigation while links or axe were being read. It was\n * not analyzed. Tell it from `AnalyzedPage` with `\"redirect\" in x`.\n */\nexport interface RedirectedPage {\n /** The absolute URL the page redirects to. */\n redirect: string;\n}\n\nexport interface PageAnalyzer {\n /**\n * Load `url` and run axe, or report where the page redirects in the\n * browser. Rejects with `A11yError` on navigation failure or timeout\n * (message names the URL and the underlying reason); the caller decides\n * whether that is fatal (seed) or a per-page `error` (crawled page).\n */\n analyze(url: string, opts: AnalyzeOptions): Promise<AnalyzedPage | RedirectedPage>;\n /** Close the browser. Safe to call when nothing was launched, and twice. */\n close(): Promise<void>;\n}\n\nexport type BrowserChannel = \"chrome\" | \"msedge\" | \"chromium\";\n\n/** The message when nothing launches; the same words the CLI's ladder promises. */\nexport const NO_BROWSER_MESSAGE =\n \"No browser found. Install one with `npx playwright install chromium`, or install Google Chrome or Microsoft Edge.\";\n\n/** The launch order: what the machine already has first, Playwright's own download last. */\nconst CHANNELS: readonly BrowserChannel[] = [\"chrome\", \"msedge\", \"chromium\"];\n\nfunction launch(channel: BrowserChannel): Promise<Browser> {\n return channel === \"chromium\"\n ? chromium.launch({ headless: true })\n : chromium.launch({ channel, headless: true });\n}\n\n/** The first channel that launches, still open, or `null` when none does. */\nasync function launchAny(): Promise<{ browser: Browser; channel: BrowserChannel } | null> {\n for (const channel of CHANNELS) {\n try {\n return { browser: await launch(channel), channel };\n } catch {\n // Not installed, or not launchable here. Try the next one.\n }\n }\n return null;\n}\n\n/**\n * Try, in order: `chromium.launch({ channel: \"chrome\", headless: true })`,\n * `{ channel: \"msedge\" }`, `chromium.launch()` (Playwright's own download).\n * Returns the first that launches, closed again, or `null`. Exported so the\n * integration suite can `describe.skipIf`.\n */\nexport async function findBrowser(): Promise<BrowserChannel | null> {\n const found = await launchAny();\n if (found === null) return null;\n await found.browser.close();\n return found.channel;\n}\n\n/** axe's result shapes, as `@axe-core/playwright` declares them; no direct axe-core import. */\ntype AxeResults = Awaited<ReturnType<AxeBuilder[\"analyze\"]>>;\ntype AxeResult = AxeResults[\"violations\"][number];\ntype AxeNode = AxeResult[\"nodes\"][number];\n\ninterface Session {\n browser: Browser;\n context: BrowserContext;\n}\n\n/**\n * Playwright-backed analyzer. Lazy: the browser launches on the first\n * `analyze`, and one context is reused for every URL. Each URL gets a page\n * of its own, so a navigation a page starts late (a script redirect after axe\n * returned, or a meta refresh reported without waiting for it) stays in that\n * page's tab instead of aborting the next load. A page is closed once the\n * next one exists, never before: Chrome can drop a context left with no page,\n * and `newPage` then fails with \"Failed to open a new tab\".\n * - `page.goto(url, { waitUntil: \"load\", timeout })`; `finalUrl` is `page.url()` after it\n * - one in-page evaluation reads every `a[href]`'s absolute `href` and the\n * first meta refresh; a refresh to another page (by `dedupeKey`) is a\n * redirect, returned before axe runs\n * - a read or axe that rejects because the page navigated: wait up to\n * `timeout` for the URL to change and return the redirect, else rethrow\n * - `new AxeBuilder({ page })`, `.withTags(tags)` only when `tags.length > 0`, `.analyze()`\n * - map axe `Result` → `Violation` (`severity` from `severityOf(impact)`, `impact` kept as\n * axe's word with `null`/`undefined` → \"minor\"; node `target` joined with \" \";\n * `failureSummary ?? \"\"`), `passes.length`, `incomplete.length`\n * - launch failure on every channel → `A11yError(NO_BROWSER_MESSAGE)`\n */\nexport function createPlaywrightAnalyzer(): PageAnalyzer {\n let session: Promise<Session> | null = null;\n let current: Page | null = null;\n\n const open = (): Promise<Session> => {\n session ??= (async () => {\n const found = await launchAny();\n if (found === null) throw new A11yError(NO_BROWSER_MESSAGE);\n const context = await found.browser.newContext();\n return { browser: found.browser, context };\n })();\n return session;\n };\n\n return {\n async analyze(url, opts) {\n const { context } = await open();\n const page = await context.newPage();\n const previous = current;\n current = page;\n // Its outcome is settled; a failure to close it is not this URL's. Not\n // reentrant: an overlapping call would close a page still being read,\n // so the caller (the crawl, one page at a time) sequences its calls.\n await previous?.close().catch(() => undefined);\n // Every navigation the main frame starts, first hop only, so the one\n // that follows the load can be named even when it never commits (an\n // unresolvable host leaves the browser on its own error page).\n const navigations: Request[] = [];\n const onRequest = (request: Request): void => {\n if (\n request.isNavigationRequest() &&\n request.frame() === page.mainFrame() &&\n request.redirectedFrom() === null\n ) {\n navigations.push(request);\n }\n };\n page.on(\"request\", onRequest);\n try {\n return await analyzePage(page, url, opts, navigations);\n } finally {\n page.off(\"request\", onRequest);\n }\n },\n\n async close() {\n if (session === null) return;\n const pending = session;\n session = null;\n current = null;\n try {\n const { browser } = await pending;\n await browser.close();\n } catch {\n // The launch itself failed: there is nothing to close.\n }\n },\n };\n}\n\n/**\n * One `analyze`, on a page of its own. `navigations` is filled while it\n * runs: the first hop of every main-frame navigation, in order.\n */\nasync function analyzePage(\n page: Page,\n url: string,\n opts: AnalyzeOptions,\n navigations: readonly Request[],\n): Promise<AnalyzedPage | RedirectedPage> {\n let response: Response | null;\n try {\n response = await page.goto(url, { waitUntil: \"load\", timeout: opts.timeout });\n } catch (err) {\n throw new A11yError(`Could not load ${url}: ${reason(err)}`);\n }\n // Where the load settled, HTTP redirects included. The browser being\n // anywhere else later means the page navigated on its own.\n const loaded = response?.url() ?? page.url();\n\n /**\n * The first navigation the main frame started after the load's own, or\n * `undefined` when it started none. The load's own is found by walking its\n * HTTP redirect hops back to the first request. When `goto` gave no\n * response (a service worker answered it), the listener was attached just\n * before `goto`, so its navigation is the first one recorded.\n */\n const nextNavigation = (): Request | undefined => {\n let first = response?.request() ?? null;\n for (let from = first?.redirectedFrom() ?? null; from !== null; from = from.redirectedFrom()) {\n first = from;\n }\n // No response means a service worker answered `goto`: its navigation is [0].\n const own = first === null ? 0 : navigations.indexOf(first);\n return own === -1 ? undefined : navigations[own + 1];\n };\n\n /**\n * Where the page sent the browser. The navigation request is named even\n * when it never commits, since an unresolvable host leaves the frame on the\n * browser's own error page. Without one, the frame's URL is the answer:\n * every caller has already seen it move off `loaded`, and Playwright\n * reports the request before the URL changes, so this is a fallback only.\n */\n const destination = (): string => nextNavigation()?.url() ?? page.url();\n\n /**\n * After a read rejected: when the page started a navigation, wait for the\n * main frame to commit it and return the redirect. When it started none,\n * the error was not a navigation and stands at once, rather than after a\n * second wait of `timeout`.\n */\n const navigatedAway = async (): Promise<RedirectedPage | null> => {\n if (nextNavigation() === undefined && samePage(page.url(), loaded)) return null;\n try {\n await page.waitForURL((u) => !samePage(u.href, loaded), {\n waitUntil: \"commit\",\n timeout: opts.timeout,\n });\n return { redirect: destination() };\n } catch {\n return null;\n }\n };\n\n let read: InPage;\n try {\n read = await page.evaluate(readPage);\n } catch (err) {\n const moved = await navigatedAway();\n if (moved !== null) return moved;\n throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);\n }\n const refresh = refreshTarget(read.refresh, page.url());\n if (refresh !== null) return { redirect: refresh };\n\n let builder = new AxeBuilder({ page });\n if (opts.tags.length > 0) builder = builder.withTags(opts.tags);\n let results: AxeResults;\n try {\n results = await builder.analyze();\n } catch (err) {\n const moved = await navigatedAway();\n if (moved !== null) return moved;\n throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);\n }\n // The page can navigate between two reads without either rejecting, and\n // then what was read belongs to the destination, not to `url`. Where it\n // went comes from the recorded request, with the frame's URL as fallback.\n // A script redirect that fires after axe returns is not seen at all: the\n // page was analyzed as it stood, which is what it was when it was read.\n if (!samePage(page.url(), loaded)) return { redirect: destination() };\n return {\n result: {\n url,\n violations: results.violations.map(toViolation),\n passes: results.passes.length,\n incomplete: results.incomplete.length,\n },\n links: read.links,\n finalUrl: page.url(),\n };\n}\n\n/** What one in-page evaluation reads before axe runs. */\ninterface InPage {\n /** Every `a[href]`'s absolute `href`. */\n links: string[];\n /** The first `<meta http-equiv=\"refresh\">`'s `content`, or `null`. */\n refresh: string | null;\n}\n\n/** Runs in the page. The project has no DOM lib, so the DOM is typed as narrowly as it is used. */\nfunction readPage(): InPage {\n const doc = (\n globalThis as unknown as {\n document: {\n querySelectorAll(selector: string): ArrayLike<{ href: string }>;\n querySelector(selector: string): { getAttribute(name: string): string | null } | null;\n };\n }\n ).document;\n return {\n links: Array.from(doc.querySelectorAll(\"a[href]\"), (a) => a.href),\n refresh:\n doc.querySelector('meta[http-equiv=\"refresh\" i]')?.getAttribute(\"content\") ?? null,\n };\n}\n\n/**\n * The URL part of a meta refresh's `content`: `<delay>;url=<url>` or\n * `<delay>, <url>`, `url=` optional and any case, quotes around the URL\n * optional. `null` for a bare delay, which is a reload rather than a redirect.\n */\nexport function refreshUrl(content: string): string | null {\n // The delay may be fractional (\"0.5\"), hence the dot. A malformed delay\n // such as \"...\" also passes, which costs nothing: only the URL is used.\n const match = /^\\s*[\\d.]*\\s*[;,]\\s*(?:url\\s*=\\s*)?(.*)$/is.exec(content);\n let target = match?.[1]?.trim() ?? \"\";\n const quote = target[0];\n if (quote === \"'\" || quote === '\"') {\n const end = target.indexOf(quote, 1);\n target = (end === -1 ? target.slice(1) : target.slice(1, end)).trim();\n }\n return target === \"\" ? null : target;\n}\n\n/**\n * Where a meta refresh sends the browser, resolved against the page, or\n * `null` when there is no refresh, no URL in it, or it points back at the\n * page itself.\n */\nfunction refreshTarget(content: string | null, base: string): string | null {\n if (content === null) return null;\n const target = refreshUrl(content);\n if (target === null) return null;\n let to: string;\n try {\n to = new URL(target, base).href;\n } catch {\n return null;\n }\n return samePage(to, base) ? null : to;\n}\n\n/** One page by `dedupeKey`; a string that is not an http(s) URL compares as itself. */\nfunction samePage(a: string, b: string): boolean {\n try {\n return dedupeKey(a) === dedupeKey(b);\n } catch {\n return a === b;\n }\n}\n\n/** What axe may put in `impact`; the same union `@axe-core/playwright` declares. */\ntype ImpactValue = AxeResult[\"impact\"];\n\n/**\n * axe's impact onto the family severity scale (`src/shared/severity.ts`).\n * Four levels fold onto three: `critical` and `serious` are both `error`,\n * `moderate` is `warning`, `minor` is `notice`. A missing impact, which axe\n * reports as `null` for a rule with no impact set, is the lowest level. The\n * original word is kept on the violation as `impact`, so nothing is lost for\n * anyone looking the rule up where axe names it.\n */\nexport function severityOf(impact: ImpactValue | null | undefined): Severity {\n switch (impact) {\n case \"critical\":\n case \"serious\":\n return \"error\";\n case \"moderate\":\n return \"warning\";\n default:\n return \"notice\";\n }\n}\n\n/** axe's word as it stands, or `minor` when it gave none. */\nfunction impactOf(impact: ImpactValue | null | undefined): AxeImpact {\n return impact != null && isAxeImpact(impact) ? impact : \"minor\";\n}\n\nfunction toViolation(result: AxeResult): Violation {\n return {\n id: result.id,\n severity: severityOf(result.impact),\n impact: impactOf(result.impact),\n help: result.help,\n helpUrl: result.helpUrl,\n tags: result.tags,\n nodes: result.nodes.map(toNode),\n };\n}\n\nfunction toNode(node: AxeNode): ViolationNode {\n return {\n // A shadow-DOM target is a nested list of selectors; flatten it the same way.\n target: node.target.map((t) => (Array.isArray(t) ? t.join(\" \") : t)).join(\" \"),\n html: node.html,\n summary: node.failureSummary ?? \"\",\n };\n}\n\n/**\n * The first line of a Playwright error. The rest is a call log that repeats\n * the URL and the timeout, which the caller's message already carries.\n */\nfunction reason(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n return message.split(\"\\n\")[0]?.trim() ?? message;\n}\n","/**\n * The `a11y:` key of the family config file (`manni.config.yaml`).\n *\n * Finding the file is the shared layer's job (src/shared/config-file.ts);\n * what the section may contain is decided here. The posture is meta's: an\n * unknown key or a wrong type is an error that names the file and the key,\n * because a misspelled `maxPages:` that was silently ignored would run the\n * crawl on defaults and read as a clean pass.\n */\nimport type { CollectionConfig } from \"../../shared/collections.js\";\nimport {\n findConfigFile,\n readConfigFile,\n type ConfigFileOptions,\n} from \"../../shared/config-file.js\";\nimport { A11yError, SEVERITIES, isSeverity, type Severity } from \"../types.js\";\nimport { MUST_START_WITH_SLASH, isUrlPathGlob } from \"./exclude.js\";\nimport { isHttpUrl } from \"./url.js\";\n\nexport interface A11yConfig {\n urls?: string[];\n crawl?: boolean;\n /** Cap on pages checked. Absent means no cap. */\n maxPages?: number;\n tags?: string[];\n severity?: Severity;\n timeout?: number;\n /** Globs matched against a URL's path; a match keeps the page out of the crawl. */\n exclude?: string[];\n}\n\nexport interface LoadedA11yConfig {\n config: A11yConfig;\n /** Where it came from, for messages. `null` when no file was found or `--no-config`. */\n source: string | null;\n /**\n * The family file's top-level `collections:`, passed straight through: a\n * collection's `url` is a seed source (proposal 0041, rule 12), and the\n * declaration belongs to the family rather than to `a11y:`. `[]` when no\n * file was found and when none are declared.\n */\n collections: CollectionConfig[];\n}\n\n/** The tool's key in the family file. */\nconst SECTION = \"a11y\";\n\n/**\n * The keys the section may carry. Adding a key to `A11yConfig` means adding\n * it here too, or a config using it is rejected; that coupling is the point.\n */\nconst CONFIG_KEYS = [\"urls\", \"crawl\", \"maxPages\", \"tags\", \"severity\", \"timeout\", \"exclude\"] as const;\n\nconst CONFIG_FILE: ConfigFileOptions = {\n section: SECTION,\n // The a11y tool never had a pre-family file of its own.\n legacyNames: [],\n toError: (message) => new A11yError(message),\n};\n\n/**\n * Discover (or read `explicitPath`) through the shared family-file loader with\n * `{ section: \"a11y\", legacyNames: [], toError: (m) => new A11yError(m) }`.\n * Unknown keys, wrong types, empty `urls` entries and non-`http(s)` urls are\n * `A11yError`s naming the file and key. `null` config (empty section) is `{}`.\n * The document's `collections:` come back alongside, parsed by the shared\n * loader: the key is the family's, not this section's.\n */\nexport async function loadA11yConfig(\n cwd: string,\n explicitPath?: string,\n): Promise<LoadedA11yConfig> {\n const file =\n explicitPath === undefined\n ? await findConfigFile(cwd, CONFIG_FILE)\n : await readConfigFile(explicitPath, cwd, CONFIG_FILE);\n if (file === null) return { config: {}, source: null, collections: [] };\n return {\n config: parseA11yConfig(file.value, file.source),\n source: file.source,\n collections: file.collections,\n };\n}\n\n/** Pure parser, exported for tests. */\nexport function parseA11yConfig(value: unknown, source: string): A11yConfig {\n if (value == null) return {};\n if (typeof value !== \"object\" || Array.isArray(value)) {\n throw new A11yError(`${source}: \\`${SECTION}:\\` must be a mapping.`);\n }\n const obj = value as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n if (!(CONFIG_KEYS as readonly string[]).includes(key)) {\n throw new A11yError(\n `${source}: \\`${SECTION}:\\` has unknown key \"${key}\". Supported keys: ${CONFIG_KEYS.join(\", \")}.`,\n );\n }\n }\n\n const config: A11yConfig = {};\n if (obj.urls !== undefined) config.urls = asUrls(obj.urls, source);\n if (obj.crawl !== undefined) config.crawl = asBoolean(obj.crawl, \"crawl\", source);\n if (obj.maxPages !== undefined) {\n config.maxPages = asPositiveInteger(obj.maxPages, \"maxPages\", source);\n }\n if (obj.tags !== undefined) config.tags = asStringList(obj.tags, \"tags\", source);\n if (obj.severity !== undefined) config.severity = asSeverity(obj.severity, source);\n if (obj.timeout !== undefined) {\n config.timeout = asPositiveInteger(obj.timeout, \"timeout\", source);\n }\n if (obj.exclude !== undefined) config.exclude = asExcludes(obj.exclude, source);\n return config;\n}\n\n/**\n * How a message names one entry of `a11y.exclude:`: the file it came from and\n * the entry's position in the list. Both the parse errors below and the\n * excluded-seed refusal in the check core name an entry this way, so a run\n * whose patterns came from config never mentions a flag the user did not type.\n */\nexport function excludeEntryLabel(source: string, index: number): string {\n return `${source}: \"${SECTION}.exclude[${String(index)}]\"`;\n}\n\n/**\n * Each entry has to be able to match a path, which always starts with `/`. A\n * pattern that cannot is a silent near-miss rather than a filter, so it is\n * refused here, naming whichever entry is wrong by its position in the list.\n */\nfunction asExcludes(value: unknown, source: string): string[] {\n const globs = asStringList(value, \"exclude\", source);\n globs.forEach((glob, i) => {\n const at = excludeEntryLabel(source, i);\n // An empty string fails the slash check too, so this is only about which\n // message it gets: \"must be a non-empty string\" names the mistake, where\n // \"must start with /\" would describe a pattern that is not there.\n if (glob === \"\") throw new A11yError(`${at} must be a non-empty string.`);\n if (!isUrlPathGlob(glob)) throw new A11yError(`${at} ${MUST_START_WITH_SLASH}`);\n });\n return globs;\n}\n\nfunction asStringList(value: unknown, field: string, source: string): string[] {\n if (!Array.isArray(value) || !value.every((v) => typeof v === \"string\")) {\n throw new A11yError(`${source}: \"${SECTION}.${field}\" must be a list of strings.`);\n }\n return value;\n}\n\nfunction asUrls(value: unknown, source: string): string[] {\n const urls = asStringList(value, \"urls\", source);\n urls.forEach((url, i) => {\n if (!isHttpUrl(url)) {\n throw new A11yError(\n `${source}: \"${SECTION}.urls[${i}]\" must be an http(s) URL, got \"${url}\".`,\n );\n }\n });\n return urls;\n}\n\nfunction asBoolean(value: unknown, field: string, source: string): boolean {\n if (typeof value !== \"boolean\") {\n throw new A11yError(`${source}: \"${SECTION}.${field}\" must be a boolean.`);\n }\n return value;\n}\n\nfunction asPositiveInteger(value: unknown, field: string, source: string): number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) {\n throw new A11yError(`${source}: \"${SECTION}.${field}\" must be an integer >= 1.`);\n }\n return value;\n}\n\nfunction asSeverity(value: unknown, source: string): Severity {\n if (typeof value !== \"string\" || !isSeverity(value)) {\n throw new A11yError(\n `${source}: \"${SECTION}.severity\" must be one of ${SEVERITIES.join(\", \")}.`,\n );\n }\n return value;\n}\n","/**\n * `--format github`: one workflow-command annotation per finding.\n *\n * There is no file to anchor to, since the check ran against a URL, so the\n * annotation carries the rule id as its title and names the page in the\n * message. Empty when the run is clean, so a clean CI step logs nothing.\n */\nimport {\n escapeWorkflowCommandMessage,\n escapeWorkflowCommandProperty,\n} from \"../../shared/github.js\";\nimport type { CheckRun, Severity } from \"../types.js\";\n\n/** The three levels a workflow command can carry. */\nexport type AnnotationLevel = \"error\" | \"warning\" | \"notice\";\n\n/**\n * The annotation level for a finding of the given severity.\n *\n * The family scale (`src/shared/severity.ts`) was chosen to match GitHub's,\n * so the map is one-to-one and this is the identity. It stays a function,\n * and stays exported, because it is the seam: the translation from a\n * domain's severity to an output's levels happens here and nowhere else,\n * and a reporter for a sink with another scale (SARIF's `note`) would carry\n * its own. axe's four impacts were folded onto these three by the analyzer\n * (`severityOf`), before any reporter saw the finding. Proposal 0035,\n * stress test 10.\n */\nexport function annotationLevel(severity: Severity): AnnotationLevel {\n return severity;\n}\n\nexport function renderGithub(run: CheckRun): string {\n const lines: string[] = [];\n for (const page of run.results) {\n if (page.error !== undefined) {\n // A page that did not load has no severity to translate. It is the most\n // serious thing a run can hold, so it is always an error.\n lines.push(\n `::error title=a11y/load::${escapeWorkflowCommandMessage(`${page.url}: ${page.error}`)}`,\n );\n continue;\n }\n for (const v of page.violations) {\n const n = v.nodes.length;\n const title = escapeWorkflowCommandProperty(`a11y/${v.id}`);\n const msg = escapeWorkflowCommandMessage(\n `${v.help} — ${n} node${n === 1 ? \"\" : \"s\"} on ${page.url} (${v.helpUrl})`,\n );\n lines.push(`::${annotationLevel(v.severity)} title=${title}::${msg}`);\n }\n }\n return lines.join(\"\\n\");\n}\n","/**\n * `--format json`: the `CheckRun` as it is, pretty-printed. Key order is the\n * order the types declare, so the envelope is stable from run to run.\n */\nimport type { CheckRun } from \"../types.js\";\n\nexport function renderJson(run: CheckRun): string {\n return JSON.stringify(run, null, 2);\n}\n","/**\n * `--format pretty`: the report a person reads in a terminal.\n *\n * One line per page, and under a failing page one line per rule followed by\n * the first three elements that failed it, each with axe's own statement of\n * which condition was not met. That per-element line is the deterministic\n * remediation: which rule, which selector, what to change. After the pages,\n * one `↪` line per page that redirected in the browser, hidden by `--quiet`\n * as a passing page is.\n *\n * Colors carry the meanings meta already gave them: ✓ green, ✗ red, URLs\n * cyan, and severity from dim (notice) through yellow (warning) to red\n * (error).\n *\n * Each violation line leads with the family severity, which is what the\n * floor and the exit code are decided on. axe's own word follows the rule id\n * in parentheses, `(axe: critical)`, so the line can still be matched\n * against the Deque rule page and the axe DevTools report that use it.\n */\nimport { palette, type Colors } from \"../../shared/color.js\";\nimport {\n SEVERITIES,\n type CheckRun,\n type Severity,\n type PageResult,\n type Violation,\n} from \"../types.js\";\nimport type { RenderOptions } from \"./index.js\";\n\n/** How many failing elements are listed under one rule before `(+N more)`. */\nconst MAX_NODES = 3;\n\nexport function renderPretty(run: CheckRun, opts: RenderOptions): string {\n const c = palette(opts.color);\n const { summary } = run;\n const lines: string[] = [];\n\n lines.push(`Checked ${summary.checked} of ${summary.discovered} pages (${where(run, c)})`);\n\n for (const page of run.results) {\n if (page.error !== undefined) {\n lines.push(`${c.red(\"✗\")} ${c.cyan(page.url)} could not load: ${page.error}`);\n continue;\n }\n if (page.violations.length === 0) {\n if (!opts.quiet) lines.push(`${c.green(\"✓\")} ${c.cyan(page.url)} score ${scoreText(page)}`);\n continue;\n }\n lines.push(\n `${c.red(\"✗\")} ${c.cyan(page.url)} score ${scoreText(page)} ${severityCounts(page.violations, c)}`,\n );\n for (const v of page.violations) lines.push(...violationLines(v, c));\n }\n\n if (!opts.quiet) {\n for (const r of run.redirects) {\n lines.push(`${c.dim(\"↪\")} ${c.cyan(r.url)} redirects to ${c.cyan(r.to)}`);\n }\n }\n\n const failed = summary.failed;\n const footer =\n `${summary.violations} violation${summary.violations === 1 ? \"\" : \"s\"} on ${failed} of ${summary.checked} pages` +\n (summary.skipped > 0 ? `; ${summary.skipped} skipped (--max-pages)` : \"\") +\n (summary.duplicates > 0\n ? `; ${summary.duplicates} duplicate${summary.duplicates === 1 ? \"\" : \"s\"} dropped`\n : \"\") +\n (summary.redirects > 0\n ? `; ${summary.redirects} redirect${summary.redirects === 1 ? \"\" : \"s\"}`\n : \"\") +\n (summary.excluded > 0 ? `; ${summary.excluded} excluded` : \"\");\n lines.push(\"\");\n lines.push(failed > 0 ? c.red(footer) : c.green(footer));\n return lines.join(\"\\n\");\n}\n\n/**\n * How the pages were found. `--no-crawl` never looks for a sitemap, so a null\n * `sitemap` alone cannot tell \"no crawl\" from \"crawled, found no sitemap\".\n *\n * A sitemap that was found is not a sitemap that supplied pages. One whose\n * every `<loc>` is on another host parses and yields nothing, which is what a\n * built sitemap does when the built site is served on a local preview. So a\n * named sitemap carries its count whenever links did some of the work, and\n * the run that was wholly a sitemap's says so with the URL alone.\n */\nfunction where(run: CheckRun, c: Colors): string {\n const { summary } = run;\n if (!summary.crawl) return \"no crawl\";\n if (summary.sitemap === null) return \"no sitemap; followed links\";\n const found = `sitemap: ${c.cyan(summary.sitemap)}`;\n const n = summary.sitemapPages;\n if (n === 0) return `${found}, 0 pages; followed links`;\n if (n >= summary.discovered) return found;\n return `${found}, ${n} page${n === 1 ? \"\" : \"s\"}; followed links for the rest`;\n}\n\nfunction scoreText(page: PageResult): string {\n return page.score === null ? \"n/a\" : String(page.score);\n}\n\n/** `2 errors, 1 notice`: most severe first, pluralised, zeros omitted. */\nfunction severityCounts(violations: Violation[], c: Colors): string {\n const counts: Record<Severity, number> = { notice: 0, warning: 0, error: 0 };\n for (const v of violations) counts[v.severity] += 1;\n return [...SEVERITIES]\n .reverse()\n .filter((severity) => counts[severity] > 0)\n .map((severity) => {\n const n = counts[severity];\n return paintSeverity(`${n} ${severity}${n === 1 ? \"\" : \"s\"}`, severity, c);\n })\n .join(\", \");\n}\n\nfunction violationLines(v: Violation, c: Colors): string[] {\n const n = v.nodes.length;\n const lines = [\n ` ${paintSeverity(v.severity, v.severity, c)} ${v.id} ${c.dim(`(axe: ${v.impact})`)} ${n} node${n === 1 ? \"\" : \"s\"} ${v.help} ${c.dim(v.helpUrl)}`,\n ];\n for (const node of v.nodes.slice(0, MAX_NODES)) {\n lines.push(` ${node.target} → ${oneLine(node.summary)}`);\n }\n if (n > MAX_NODES) lines.push(c.dim(` (+${n - MAX_NODES} more)`));\n return lines;\n}\n\n/**\n * axe's failureSummary is a heading line followed by one indented line per\n * unmet condition. On one line: the heading, then the conditions joined by\n * `; `.\n */\nfunction oneLine(summary: string): string {\n const [head, ...rest] = summary\n .split(\"\\n\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (head === undefined) return \"\";\n return rest.length === 0 ? head : `${head} ${rest.join(\"; \")}`;\n}\n\nfunction paintSeverity(text: string, severity: Severity, c: Colors): string {\n switch (severity) {\n case \"notice\":\n return c.dim(text);\n case \"warning\":\n return c.yellow(text);\n case \"error\":\n return c.red(text);\n }\n}\n","/**\n * The a11y reporters render a `CheckRun` to a string. The command layer writes\n * the result to stdout; diagnostics go to stderr separately.\n */\nimport { A11yError, type CheckRun } from \"../types.js\";\nimport { renderGithub } from \"./github.js\";\nimport { renderJson } from \"./json.js\";\nimport { renderPretty } from \"./pretty.js\";\n\nexport { renderGithub, annotationLevel } from \"./github.js\";\nexport type { AnnotationLevel } from \"./github.js\";\nexport { renderJson } from \"./json.js\";\nexport { renderPretty } from \"./pretty.js\";\n\n/**\n * Every value `--format` accepts, in the order help and error messages list\n * them. Stated once, so the union, the guard, the option text and the error\n * cannot drift apart. SARIF and JUnit are deferred (proposal 0035).\n */\nexport const A11Y_FORMATS = [\"pretty\", \"json\", \"github\"] as const;\nexport type A11yFormat = (typeof A11Y_FORMATS)[number];\n\n/** `\"pretty | json | github\"`, for messages and help text. */\nexport const A11Y_FORMAT_LIST: string = A11Y_FORMATS.join(\" | \");\n\nexport function isA11yFormat(value: string): value is A11yFormat {\n return (A11Y_FORMATS as readonly string[]).includes(value);\n}\n\nexport interface RenderOptions {\n /** From shared `shouldColor`. */\n color: boolean;\n /** pretty only: hide pages with no remaining violations. */\n quiet: boolean;\n}\n\n/** Returns the full text without a trailing newline; `github` returns \"\" when clean. */\nexport function render(format: A11yFormat, run: CheckRun, opts: RenderOptions): string {\n switch (format) {\n case \"pretty\":\n return renderPretty(run, opts);\n case \"json\":\n return renderJson(run);\n case \"github\":\n return renderGithub(run);\n default: {\n // Exhaustive: a format added to `A11Y_FORMATS` without a case here is a\n // compile error; the throw is for a caller who bypassed the guard.\n const unreachable: never = format;\n throw new A11yError(\n `Unknown report format ${JSON.stringify(unreachable)}. Use ${A11Y_FORMAT_LIST}.`,\n );\n }\n }\n}\n","/**\n * Progress on stderr, while the crawl runs.\n *\n * The browser launch alone takes seconds and a fifty-page site takes a\n * minute, and a command that says nothing for that long reads as hung. So\n * the run narrates itself on stderr, where a diagnostic belongs; stdout stays\n * the report, and `-f json | jq` keeps working.\n *\n * Two renderings. On a terminal, one status line rewritten in place\n * (`\\r`, erase line, then the text, no newline) and cleared on `done`, so the\n * report on stdout is not preceded by a dangling line. Off a terminal, one\n * plain line per event, newline-terminated and prefixed the way `warn()`\n * writes, with no escape codes, since a CI log keeps every line.\n *\n * Colours keep the meanings meta gave them: ✓ green, ✗ red, URLs cyan, the\n * counter dim. They apply on a terminal only.\n */\nimport { palette, type Colors } from \"../../shared/color.js\";\nimport { programName } from \"../../shared/program-name.js\";\nimport type { ProgressEvent, ProgressListener } from \"../types.js\";\n\nexport interface ProgressOptions {\n /** Where to write: `process.stderr`, or a fake in tests. */\n stream: { write(s: string): boolean; isTTY?: boolean; columns?: number };\n /** Paint the status line. Ignored off a terminal. */\n color: boolean;\n /** Rewrite one line in place (true) or append a line per event (false). */\n tty: boolean;\n}\n\n/** Carriage return, then erase the whole line: the start of every status line. */\nexport const CLEAR_LINE = \"\\r\\x1b[2K\";\n\n/** The width a status line is fitted to when the stream does not say. */\nconst DEFAULT_COLUMNS = 80;\n\n/** Returns a listener that renders events to `stream`. */\nexport function createProgressReporter(opts: ProgressOptions): ProgressListener {\n return opts.tty ? terminalReporter(opts) : plainReporter(opts.stream);\n}\n\nfunction plural(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`;\n}\n\n/** `[3/51]`, from the last `page` event; `checked` carries no `queued` of its own. */\nfunction counter(index: number, queued: number): string {\n return `[${index}/${queued}]`;\n}\n\nfunction terminalReporter(opts: ProgressOptions): ProgressListener {\n const { stream } = opts;\n const c = palette(opts.color);\n let queued = 0;\n\n const show = (text: string): void => {\n stream.write(`${CLEAR_LINE}${text}`);\n };\n /** `prefix`, the URL, then `suffix`; the URL shortened in the middle to fit the line. */\n const showUrl = (prefix: string, url: string, suffix = \"\"): void => {\n const columns = stream.columns ?? DEFAULT_COLUMNS;\n const fitted = truncateMiddle(url, columns - visibleLength(prefix) - suffix.length);\n show(`${prefix}${c.cyan(fitted)}${suffix}`);\n };\n\n return (event: ProgressEvent): void => {\n switch (event.kind) {\n case \"browser\":\n show(\"Starting browser…\");\n return;\n case \"sitemap\":\n if (event.source === null) show(\"No sitemap; following links\");\n else showUrl(\"Sitemap: \", event.source, ` (${plural(event.urls, \"page\")})`);\n return;\n case \"excluded\":\n show(`Excluded ${plural(event.urls, \"page\")} (${plural(event.patterns, \"pattern\")})`);\n return;\n case \"page\":\n queued = event.queued;\n showUrl(`${c.dim(counter(event.index, queued))} `, event.url);\n return;\n case \"checked\":\n showUrl(`${c.dim(counter(event.index, queued))} ${outcome(event, c)}`, event.url);\n return;\n case \"redirected\":\n showUrl(`${c.dim(counter(event.index, queued))} ${c.dim(\"↪\")} redirects to `, event.to);\n return;\n case \"done\":\n stream.write(CLEAR_LINE);\n return;\n }\n };\n}\n\n/** `✓ `, `✗ 2 violations `, or `✗ could not load: … `: what goes between the counter and the URL. */\nfunction outcome(event: Extract<ProgressEvent, { kind: \"checked\" }>, c: Colors): string {\n if (event.error !== undefined) return `${c.red(\"✗\")} could not load: ${event.error} `;\n if (event.violations === 0) return `${c.green(\"✓\")} `;\n return `${c.red(\"✗\")} ${plural(event.violations, \"violation\")} `;\n}\n\nfunction plainReporter(stream: ProgressOptions[\"stream\"]): ProgressListener {\n let queued = 0;\n const say = (text: string): void => {\n stream.write(`${programName()}: ${text}\\n`);\n };\n\n return (event: ProgressEvent): void => {\n switch (event.kind) {\n case \"browser\":\n say(\"starting browser\");\n return;\n case \"sitemap\":\n if (event.source === null) say(\"no sitemap; following links\");\n else say(`sitemap ${event.source} (${plural(event.urls, \"page\")})`);\n return;\n case \"excluded\":\n say(`excluded ${plural(event.urls, \"page\")} (${plural(event.patterns, \"pattern\")})`);\n return;\n case \"page\":\n queued = event.queued;\n say(`${counter(event.index, queued)} ${event.url}`);\n return;\n case \"checked\": {\n const head = counter(event.index, queued);\n if (event.error !== undefined) say(`${head} could not load: ${event.error}`);\n else if (event.violations === 0) say(`${head} ✓`);\n else say(`${head} ✗ ${plural(event.violations, \"violation\")}`);\n return;\n }\n case \"redirected\":\n say(`${counter(event.index, queued)} ↪ redirects to ${event.to}`);\n return;\n case \"done\":\n say(`checked ${plural(event.checked, \"page\")}, ${event.skipped} skipped`);\n return;\n }\n };\n}\n\n/** Length as the terminal shows it, with ANSI sequences taken out. */\nfunction visibleLength(text: string): number {\n return text.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n}\n\n/**\n * Shorten `text` to `room` characters by replacing its middle with `…`, so\n * both the host and the page's own name survive. Never shorter than a few\n * characters, however narrow the terminal.\n */\nfunction truncateMiddle(text: string, room: number): string {\n const max = Math.max(room, 8);\n if (text.length <= max) return text;\n const head = Math.ceil((max - 1) / 2);\n const tail = max - 1 - head;\n return `${text.slice(0, head)}…${text.slice(text.length - tail)}`;\n}\n"],"mappings":"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","names":["outcome","outcome","crawl"]}
|
|
1
|
+
{"version":3,"sources":["../src/a11y/cli.ts","../src/a11y/types.ts","../src/a11y/core/url.ts","../src/a11y/core/crawl.ts","../src/a11y/core/exclude.ts","../src/a11y/core/seeds.ts","../src/a11y/core/sitemap.ts","../src/a11y/commands/check.ts","../src/a11y/core/analyzer.ts","../src/a11y/core/config.ts","../src/a11y/reporters/github.ts","../src/a11y/reporters/json.ts","../src/a11y/reporters/pretty.ts","../src/a11y/reporters/index.ts","../src/a11y/reporters/progress.ts"],"sourcesContent":["/**\n * The a11y tool's CLI. A thin commander wrapper over `runCheck`, following\n * clig.dev the way meta's does: primary output to stdout, diagnostics to\n * stderr, color only on a TTY (never under NO_COLOR/--no-color), and the\n * exit codes 0 ok, 1 findings, 2 operational/usage.\n *\n * Every flag is validated here and turned into an `A11yError` before the\n * browser is asked to launch, so a typo costs a message and not a launch.\n */\nimport { Command } from \"commander\";\nimport pkg from \"../../package.json\" with { type: \"json\" };\nimport { shouldColor } from \"../shared/color.js\";\nimport { FAMILY_CONFIG_NAMES } from \"../shared/config-file.js\";\nimport { fail } from \"../shared/run.js\";\nimport { CHECK_DEFAULTS, runCheck } from \"./commands/check.js\";\nimport { createPlaywrightAnalyzer } from \"./core/analyzer.js\";\nimport { excludeEntryLabel, loadA11yConfig, type LoadedA11yConfig } from \"./core/config.js\";\nimport { MUST_START_WITH_SLASH, isUrlPathGlob, type ExcludeGlob } from \"./core/exclude.js\";\nimport { resolveSeeds } from \"./core/seeds.js\";\nimport { A11Y_FORMAT_LIST, isA11yFormat, render } from \"./reporters/index.js\";\nimport { CLEAR_LINE, createProgressReporter } from \"./reporters/progress.js\";\nimport {\n A11yError,\n SEVERITY_LIST,\n isSeverity,\n type Severity,\n type ProgressListener,\n} from \"./types.js\";\n\n/**\n * `--collection <name>` and `--exclude <glob>`: one value per occurrence,\n * never split on commas. The two lines are meta's `collect`, copied rather\n * than imported: a tool does not reach into a sibling tool's CLI.\n */\nfunction collect(value: string, prev: string[]): string[] {\n return prev.concat([value]);\n}\n\n/** `--tags <list>`: commas separate, whitespace around them is trimmed, empty items are dropped. */\nfunction splitList(value: string): string[] {\n return value\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\n/**\n * Whether to paint what goes to `stream`. The report and the progress line\n * are decided separately, since stdout may be a pipe while stderr is still\n * the terminal (`-f json | jq`), and the other way round.\n */\nfunction resolveColor(program: Command, stream: NodeJS.WriteStream): boolean {\n // commander maps --no-color to opts.color === false.\n const noColor = program.opts().color === false;\n // `isTTY` is undefined off a terminal, never false; `shouldColor` treats a\n // missing one as \"not a terminal\".\n return shouldColor({ noColor, isTTY: stream.isTTY });\n}\n\ninterface Progress {\n /** Hears the run's events; `undefined` keeps the run silent. */\n listener: ProgressListener | undefined;\n /**\n * Erase the status line. On a terminal it is rewritten in place, and a run\n * that dies mid-crawl (no browser, a seed that will not load) would leave\n * it there for the error to land on. A no-op everywhere else.\n */\n clear: () => void;\n}\n\n/**\n * `--progress` and `--no-progress` decide; with neither, progress is on\n * exactly when stderr is a terminal, so a CI log stays as it was.\n */\nfunction resolveProgress(program: Command, flag: boolean | undefined): Progress {\n // @types/node says boolean; off a terminal it is undefined (see resolveColor).\n const tty = (process.stderr.isTTY as boolean | undefined) ?? false;\n const silent: Progress = { listener: undefined, clear: () => undefined };\n if (!(flag ?? tty)) return silent;\n const listener = createProgressReporter({\n stream: process.stderr,\n color: resolveColor(program, process.stderr),\n tty,\n });\n if (!tty) return { listener, clear: silent.clear };\n return {\n listener,\n clear: () => {\n process.stderr.write(CLEAR_LINE);\n },\n };\n}\n\n/**\n * A positive integer flag, in canonical form. Anything else is a usage error:\n * `0`, `-3`, `2.5`, `many`, and `01`, since a leading zero is not how anyone\n * writes a page count and would read as an octal literal elsewhere.\n * Exported for its unit test only.\n */\nexport function positiveInteger(value: string, flag: string): number {\n if (!/^[1-9]\\d*$/.test(value)) {\n throw new A11yError(`${flag} must be an integer >= 1.`);\n }\n return Number(value);\n}\n\n/**\n * `--max-pages <n>` and `--no-max-pages` share one option value, the way `-c`\n * and `--no-config` do, so the pair has three states rather than two:\n * a number the user typed, `\"uncapped\"` for the negation, and `undefined`\n * when neither half was typed and the config key still decides.\n */\nexport type MaxPagesFlag = number | \"uncapped\" | undefined;\n\n/**\n * The typed half of that pair, in canonical form. commander hands the\n * negation over as `false`, which is not the same as saying nothing: it is\n * the typed way to ask for a run with no cap at all. Parsed before the config\n * file is read, so a bad value costs a message and nothing else.\n * Exported for its unit test only.\n */\nexport function parseMaxPages(value: string | false | undefined): MaxPagesFlag {\n if (value === false) return \"uncapped\";\n if (value === undefined) return undefined;\n return positiveInteger(value, \"--max-pages\");\n}\n\n/**\n * The cap in force: a typed flag beats the config key, and `undefined` out\n * means no cap, which is what `runCheck` reads as \"check everything found\".\n * Exported for its unit test only.\n */\nexport function resolveMaxPages(\n flag: MaxPagesFlag,\n configured: number | undefined,\n): number | undefined {\n if (flag === \"uncapped\") return undefined;\n return flag ?? configured;\n}\n\n/**\n * Whether to crawl: `--crawl` and `--no-crawl` are both declared, so\n * commander holds no default for the pair and `undefined` means the user\n * typed neither. Same three-way precedence as meta: CLI flag > config key >\n * built-in default.\n * Exported for its unit test only.\n */\nexport function resolveCrawl(flag: boolean | undefined, configured: boolean | undefined): boolean {\n return flag ?? configured ?? CHECK_DEFAULTS.crawl;\n}\n\n/**\n * Every `--exclude` value has to be able to match a path, which always starts\n * with `/`. Checked before the config file is read and long before a browser\n * launches, so a pattern that could only ever miss costs a message. Each\n * survivor carries the flag spelling a later message names it by.\n * Exported for its unit test only.\n */\nexport function assertExcludeGlobs(globs: readonly string[]): ExcludeGlob[] {\n return globs.map((glob) => {\n if (!isUrlPathGlob(glob)) {\n throw new A11yError(`--exclude \"${glob}\" ${MUST_START_WITH_SLASH}`);\n }\n return { glob, source: `--exclude \"${glob}\"` };\n });\n}\n\n/**\n * The same list as it arrives from `a11y.exclude:`, named the way the config\n * errors name it. A run whose patterns came from a file never reports them as\n * a flag, which would send a reader looking through a shell history for\n * something they never typed.\n * Exported for its unit test only.\n */\nexport function configExcludeGlobs(\n globs: readonly string[],\n source: string | null,\n): ExcludeGlob[] {\n // The CLI reaches here only with a file behind it, since `cfg.exclude` is\n // set, so the fallback is for a programmatic caller that passed a section\n // with no source. It names the file a reader would look for rather than\n // labelling an entry with \"null\".\n const file = source ?? FAMILY_CONFIG_NAMES[0] ?? \"manni.config.yaml\";\n return globs.map((glob, i) => ({ glob, source: excludeEntryLabel(file, i) }));\n}\n\nfunction assertSeverity(value: string): Severity {\n if (!isSeverity(value)) {\n throw new A11yError(`Unknown --severity \"${value}\". Use ${SEVERITY_LIST}.`);\n }\n return value;\n}\n\ninterface CheckCliOptions {\n /** `-f, --format` (default \"pretty\"). */\n format: string;\n /** `--crawl` → true, `--no-crawl` → false, neither → undefined (config decides). */\n crawl?: boolean;\n /** `--max-pages <n>` → the string; `--no-max-pages` → commander's false; neither → undefined. */\n maxPages?: string | false;\n /** `--tags <list>`, split on \",\" here (same helper shape as meta's --ext). */\n tags?: string;\n /** `--severity <level>`. */\n severity: string;\n /** `--timeout <ms>`, parsed to int here. */\n timeout: string;\n /** `-q, --quiet`. */\n quiet?: boolean;\n /** `--progress` → true, `--no-progress` → false, neither → undefined (auto). */\n progress?: boolean;\n /** `--collection <name>`, one value per occurrence. `[]` when never given. */\n collection: string[];\n /** `--exclude <glob>`, one value per occurrence. `[]` when never given. */\n exclude: string[];\n /** `-c <path>` | `--no-config` → false. */\n config?: string | false;\n}\n\n/** Commander program named \"a11y\"; the umbrella mounts it. No entry point. */\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name(\"a11y\")\n .description(\"Crawl a site and check every page for accessibility violations with axe-core\")\n .version(pkg.version, \"-V, --version\")\n .option(\"--no-color\", \"disable colored output\")\n // A pointer, not the whole help screen; the message before it already\n // names the offending flag.\n .showHelpAfterError(\"(add --help for usage)\")\n // MUST come before the `.command()` call below. `copyInheritedSettings`\n // copies `_exitCallback` **by value** at subcommand-creation time, so an\n // `exitOverride()` installed afterwards leaves the subcommand calling\n // `process.exit(1)` while the program-level case looks fixed.\n .exitOverride();\n\n program\n .command(\"check\")\n .description(\"Crawl from the given URLs and check every page with axe-core\")\n .argument(\n \"[urls...]\",\n \"http(s) seed URLs; falls back to a collection's url: or a11y.urls in manni.config.yaml\",\n )\n .option(\"-f, --format <format>\", `output: ${A11Y_FORMAT_LIST}`, \"pretty\")\n // `--crawl` first, so that `--no-crawl` leaves the pair's default\n // undefined instead of commander's `true` for a lone negation. Undefined\n // is what lets `crawl:` in config be read as the next rung down.\n .option(\"--crawl\", \"crawl from the given URLs: sitemap and link following (default)\")\n .option(\"--no-crawl\", \"check exactly the given URLs: no sitemap, no link following\")\n // No commander default on purpose: the built-in behaviour is to check\n // everything discovered, and \"no cap\" is not a value commander could hold.\n .option(\n \"--max-pages <n>\",\n \"cap on pages checked; the rest are reported as skipped (default: no cap)\",\n )\n .option(\"--no-max-pages\", \"check every page found, ignoring a configured maxPages\")\n .option(\n \"--exclude <glob>\",\n \"URL path glob to keep out of the crawl; repeatable\",\n collect,\n [],\n )\n .option(\"--tags <list>\", \"comma-separated axe tags to restrict the rules to\")\n .option(\n \"--severity <level>\",\n `minimum severity reported: ${SEVERITY_LIST}`,\n CHECK_DEFAULTS.severity,\n )\n .option(\n \"--timeout <ms>\",\n \"per-page navigation timeout in milliseconds\",\n String(CHECK_DEFAULTS.timeout),\n )\n .option(\"-q, --quiet\", \"in pretty output, hide pages with no violations\")\n // `--progress` first, so that adding `--no-progress` leaves the default\n // undefined (auto) instead of commander's `true` for a lone negation.\n .option(\"--progress\", \"report progress on stderr (default: only on a terminal)\")\n .option(\"--no-progress\", \"never report progress\")\n .option(\n \"--collection <name>\",\n \"configured collection to run over; repeatable\",\n collect,\n [],\n )\n .option(\"-c, --config <path>\", \"path to a manni config file\")\n .option(\"--no-config\", \"ignore any discovered config file\")\n .action(async (urls: string[], options: CheckCliOptions, command: Command) => {\n // Settled once the flags are validated; until then there is no line to clear.\n let clearProgress = (): void => undefined;\n try {\n // A value commander filled in from the option's default yields to the\n // config file; one the user typed wins over it. Same three-way\n // precedence as meta: CLI flag > config key > default.\n const typed = (name: string): boolean =>\n command.getOptionValueSource(name) === \"cli\";\n\n const format = options.format;\n if (!isA11yFormat(format)) {\n throw new A11yError(`Unknown --format \"${format}\". Use ${A11Y_FORMAT_LIST}.`);\n }\n const severity = assertSeverity(options.severity);\n const maxPages = parseMaxPages(options.maxPages);\n const timeout = positiveInteger(options.timeout, \"--timeout\");\n const exclude = assertExcludeGlobs(options.exclude);\n\n const loaded: LoadedA11yConfig =\n options.config === false\n // --no-config drops the collections with the section: the file\n // the user told the run to ignore is where both live.\n ? { config: {}, source: null, collections: [] }\n : await loadA11yConfig(process.cwd(), options.config);\n const cfg = loaded.config;\n\n // Before the analyzer exists: a run with nothing to check, or a\n // --collection that cannot be honoured, costs a message and not a\n // browser launch.\n const seeds = resolveSeeds({\n urls,\n collections: options.collection,\n ...(cfg.urls === undefined ? {} : { configUrls: cfg.urls }),\n declared: loaded.collections,\n source: loaded.source,\n });\n\n const parent = command.parent ?? command;\n const progress = resolveProgress(parent, options.progress);\n clearProgress = progress.clear;\n\n const run = await runCheck(\n {\n urls: seeds,\n crawl: resolveCrawl(options.crawl, cfg.crawl),\n // Typed flag > config key > no cap. There is no default to fall\n // to, and `--no-max-pages` is the typed way to ask for none.\n maxPages: resolveMaxPages(maxPages, cfg.maxPages),\n tags:\n options.tags !== undefined\n ? splitList(options.tags)\n : (cfg.tags ?? CHECK_DEFAULTS.tags),\n // A typed flag replaces the config list entirely; the two do not\n // merge. Merging would only ever let a run exclude *more* than\n // the repository's default, so checking one excluded section on\n // purpose would need --no-config, which also drops collections:.\n exclude: typed(\"exclude\")\n ? exclude\n : configExcludeGlobs(cfg.exclude ?? [], loaded.source),\n severity: typed(\"severity\") ? severity : (cfg.severity ?? CHECK_DEFAULTS.severity),\n timeout: typed(\"timeout\") ? timeout : (cfg.timeout ?? CHECK_DEFAULTS.timeout),\n },\n { analyzer: createPlaywrightAnalyzer(), onProgress: progress.listener },\n );\n\n const color = resolveColor(parent, process.stdout);\n const text = render(format, run, { color, quiet: Boolean(options.quiet) });\n // Only `github` may say nothing on a clean run; every other format\n // owes its envelope even when it is empty.\n if (text.length > 0 || format !== \"github\") {\n process.stdout.write(`${text}\\n`);\n }\n process.exitCode = run.summary.failed > 0 ? 1 : 0;\n } catch (err) {\n clearProgress();\n fail(err);\n }\n });\n\n return program;\n}\n\n","/**\n * The shapes the a11y tool produces and the error it throws.\n *\n * Everything downstream of the browser — the crawl, the check core, the\n * reporters — speaks in these types, so the analyzer is the only module that\n * knows axe's own result shape.\n */\nimport { ToolError } from \"../shared/errors.js\";\nimport type { Severity } from \"../shared/severity.js\";\n\n/**\n * The severity scale is the family's, not a11y's. It is re-exported here so\n * the tool's own modules and tests have one import for everything they speak.\n */\nexport {\n SEVERITIES,\n SEVERITY_LIST,\n isSeverity,\n meetsSeverity,\n type Severity,\n} from \"../shared/severity.js\";\n\n/** Operational/usage failure of the a11y tool: one line on stderr, exit 2. */\nexport class A11yError extends ToolError {\n constructor(message: string) {\n super(message);\n this.name = \"A11yError\";\n }\n}\n\n/**\n * axe's own scale, which it calls impact. Least to most severe. The analyzer\n * maps these onto the family `Severity` (`severityOf`) and keeps the original\n * on the violation as `impact`.\n */\nexport const AXE_IMPACTS = [\"minor\", \"moderate\", \"serious\", \"critical\"] as const;\nexport type AxeImpact = (typeof AXE_IMPACTS)[number];\n\nexport function isAxeImpact(value: string): value is AxeImpact {\n return (AXE_IMPACTS as readonly string[]).includes(value);\n}\n\nexport interface ViolationNode {\n /** axe's CSS selector path to the element (joined `target`). */\n target: string;\n /** The element's outer HTML, as axe reports it (truncated to 200 chars in pretty output only). */\n html: string;\n /** axe's failureSummary: what to fix, in prose. */\n summary: string;\n}\n\nexport interface Violation {\n /** axe rule id, e.g. \"image-alt\". */\n id: string;\n /** The family level, mapped from `impact`. What the floor and the reporters use. */\n severity: Severity;\n /**\n * axe's own value, kept for lookup against Deque's rule pages and the axe\n * DevTools report a team already reads. An unknown or `null` impact is\n * recorded as `\"minor\"`.\n */\n impact: AxeImpact;\n /** Short rule description, e.g. \"Images must have alternate text\". */\n help: string;\n /** Deque University rule page. */\n helpUrl: string;\n /** axe tags, e.g. [\"cat.text-alternatives\", \"wcag2a\", \"wcag111\"]. */\n tags: string[];\n nodes: ViolationNode[];\n}\n\nexport interface PageResult {\n /** Normalized URL that was checked. */\n url: string;\n /** How the URL entered the run. */\n source: \"seed\" | \"sitemap\" | \"link\";\n /** Violations at or above the severity floor. Empty when `error` is set. */\n violations: Violation[];\n /** Count of axe rules that passed on this page. */\n passes: number;\n /** Count of rules axe could not decide (reported, never counted as failures). */\n incomplete: number;\n /**\n * round(100 × passes ÷ (passes + violations.length)), the share of applicable\n * axe rules that passed after severity filtering. Not a Lighthouse score.\n * `null` when the page did not load, or when no rule applied.\n */\n score: number | null;\n /** Set when the page could not be loaded or analyzed; the page counts as failed. */\n error?: string;\n}\n\n/**\n * A page that sent the browser elsewhere once it had loaded, by a meta refresh\n * or a script. It is not analyzed and is not a result: its destination joins\n * the crawl in its place, under the same rules as any other URL.\n */\nexport interface Redirect {\n /** The page that redirected, as queued. */\n url: string;\n /** Where it redirects, normalized. Present whether or not the destination was checked. */\n to: string;\n /** How the redirecting page entered the run. Its destination inherits it. */\n source: PageResult[\"source\"];\n}\n\nexport interface CheckSummary {\n /** Distinct URLs that entered the frontier (seeds + sitemap + links). */\n discovered: number;\n /** Pages actually analyzed. */\n checked: number;\n /** Discovered but not checked because the `maxPages` cap was reached; `0` without a cap. */\n skipped: number;\n /**\n * Discovered, then dropped unloaded because the browser had already landed\n * on the same page under another spelling (a redirect target). Neither\n * checked nor skipped:\n * `checked + skipped + duplicates + redirects === discovered`.\n */\n duplicates: number;\n /**\n * Pages discovered, then not analyzed because they redirect in the browser\n * (a meta refresh or a script). Each one is listed in `CheckRun.redirects`.\n */\n redirects: number;\n /**\n * Distinct URLs an `--exclude` pattern kept out of the crawl, counted once\n * each however many pages linked to them, and keyed the way the frontier\n * dedupes. It sits **outside** the identity above, because an excluded URL\n * never entered the frontier. `0` on a run that excludes nothing.\n */\n excluded: number;\n /**\n * Pages with ≥1 remaining violation, whatever its severity, or an `error`.\n * Drives exit 1. The floor is the gate here, unlike `meta` and `cite`, which\n * fail on error-severity findings alone; proposal 0064.\n */\n failed: number;\n /** Total remaining violations across pages (rules, not nodes). */\n violations: number;\n /** Violations by family severity, always all three keys, zero-filled. */\n bySeverity: Record<Severity, number>;\n /** The sitemap URL that was found, or `null` if none was. Finding one is not using one: see `sitemapPages`. */\n sitemap: string | null;\n /**\n * Same-host, in-scope page URLs the sitemap listed. `0` when no sitemap was\n * found, and `0` when one was found whose every `<loc>` was out of scope — a\n * built sitemap listing production URLs, read on a local preview, is the\n * ordinary way that happens.\n *\n * This is the count the progress line reports, so it is taken before the\n * crawl dedupes against the seeds. A seed that the sitemap also lists is\n * counted here, which means the number can overstate what the sitemap alone\n * contributed to the frontier.\n */\n sitemapPages: number;\n /**\n * Whether the run discovered pages beyond the seeds (sitemap and same-host\n * links). `false` under `--no-crawl`, where `sitemap` is always `null` and\n * no link was followed.\n */\n crawl: boolean;\n}\n\nexport interface CheckRun {\n results: PageResult[];\n /** Pages that redirected in the browser, in visit order. Never in `results`. */\n redirects: Redirect[];\n summary: CheckSummary;\n}\n\n/**\n * What a run says while it is still running. The crawl emits these in order\n * (`browser`, then `page` and `checked` or `redirected` per page, then\n * `done`); the check core adds `sitemap` before them when it crawls. A\n * listener is optional, and nothing is emitted without one.\n */\nexport type ProgressEvent =\n /** About to launch the browser, before the first page. */\n | { kind: \"browser\" }\n /** Discovery finished: the sitemap that was found (or none), and how many in-scope URLs it gave. */\n | { kind: \"sitemap\"; source: string | null; urls: number }\n /**\n * Discovery settled and `--exclude` took `urls` distinct URLs out, across\n * `patterns` patterns. Emitted once, before the browser starts, and only\n * when something was excluded. There is no event per excluded URL: a\n * pattern that removes four hundred pages would bury the ones checked.\n *\n * `urls` is what the seeds and the sitemap gave the run. Links are followed\n * afterwards, and a page excluded that way is counted in `summary.excluded`\n * without reaching this event, so the two can disagree and the summary is\n * the authoritative total.\n */\n | { kind: \"excluded\"; urls: number; patterns: number }\n /** About to check page `index` (1-based); `queued` is every URL discovered so far. */\n | { kind: \"page\"; index: number; queued: number; url: string }\n /**\n * Page `index` finished. `violations` is axe's count for the page before the\n * severity floor is applied, so it can be higher than what the report keeps.\n * `error` is set when the page could not be loaded or analyzed. A seed that\n * fails ends the run instead, and is reported as the run's error.\n */\n | { kind: \"checked\"; index: number; url: string; violations: number; error?: string }\n /**\n * Page `index` sent the browser to `to` once loaded, so it was not\n * analyzed. Emitted in place of `checked`; the counter counts checked\n * pages, so the next page reuses `index`.\n */\n | { kind: \"redirected\"; index: number; url: string; to: string }\n /** The crawl is over: pages analyzed, and pages left in the frontier. */\n | { kind: \"done\"; checked: number; skipped: number };\n\nexport type ProgressListener = (event: ProgressEvent) => void;\n","/**\n * URL helpers for the crawl.\n *\n * Three questions, asked of every candidate before it enters the frontier:\n * what is its canonical spelling (so `/a#top` and `/a` are one page), is it\n * on a host the run is allowed to visit, and is it a page at all rather than\n * a stylesheet or a PDF. The WHATWG parser does most of the first one.\n */\nimport { A11yError } from \"../types.js\";\n\n/**\n * Canonical spelling for dedupe: parse with `new URL`, drop the fragment,\n * lowercase the hostname, drop a default port, keep path/query/trailing slash\n * exactly as given. Throws `A11yError` for an unparseable string.\n */\nexport function normalizeUrl(url: string): string {\n const parsed = parse(url);\n if (parsed === null) throw new A11yError(`Not a valid URL: \"${url}\".`);\n parsed.hash = \"\";\n return parsed.href;\n}\n\n/**\n * The key two spellings of one page share: `normalizeUrl`, then one trailing\n * `/` dropped from the path unless the path is exactly `/`. The query stays.\n * `/a` and `/a/` can be different resources, but on a static site they are one\n * page, and a crawl that checks both double-counts it. Only the seen set uses\n * this; the URL fetched and reported keeps the spelling it was given. Throws\n * `A11yError` for an unparseable string.\n */\nexport function dedupeKey(url: string): string {\n const parsed = new URL(normalizeUrl(url));\n if (parsed.pathname.length > 1 && parsed.pathname.endsWith(\"/\")) {\n parsed.pathname = parsed.pathname.slice(0, -1);\n }\n return parsed.href;\n}\n\n/** `http:` or `https:` and parseable. */\nexport function isHttpUrl(url: string): boolean {\n const parsed = parse(url);\n return parsed !== null && isHttpProtocol(parsed);\n}\n\n/**\n * Same site = same hostname (case-insensitive) and same non-default port.\n * `http://` and `https://` of one host are the same site; `www.example.com`\n * and `example.com` are not.\n */\nexport function sameHost(a: string, b: string): boolean {\n const left = parse(a);\n const right = parse(b);\n if (left === null || right === null) return false;\n // The parser has already dropped `:80` on http and `:443` on https, so an\n // empty port on both sides means \"the scheme's default\" and the two schemes\n // of one host compare equal, as the doc comment promises. Only an explicit\n // non-default port survives to differ.\n return (\n left.hostname.toLowerCase() === right.hostname.toLowerCase() &&\n left.port === right.port\n );\n}\n\n/**\n * A link worth loading as a page: http(s), not `mailto:`/`tel:`/`javascript:`/\n * `data:`, and the last path segment does not end in one of ASSET_EXTENSIONS.\n */\nexport function isPageLink(url: string): boolean {\n const parsed = parse(url);\n if (parsed === null || !isHttpProtocol(parsed)) return false;\n const last = parsed.pathname.slice(parsed.pathname.lastIndexOf(\"/\") + 1);\n const dot = last.lastIndexOf(\".\");\n if (dot === -1) return true;\n return !ASSET_EXTENSIONS.has(last.slice(dot).toLowerCase());\n}\n\nexport const ASSET_EXTENSIONS: ReadonlySet<string> = new Set([\n \".pdf\",\n \".zip\",\n \".gz\",\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".svg\",\n \".webp\",\n \".avif\",\n \".ico\",\n \".css\",\n \".js\",\n \".mjs\",\n \".map\",\n \".json\",\n \".xml\",\n \".txt\",\n \".mp3\",\n \".mp4\",\n \".webm\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n]);\n\nfunction parse(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n}\n\nfunction isHttpProtocol(url: URL): boolean {\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n}\n","/**\n * The crawl: which pages a run visits, and in what order.\n *\n * A breadth-first walk over a FIFO frontier. Seeds go in first, then the\n * sitemap's pages, then every same-host link each visited page lists, in the\n * order it listed them. A page enters the frontier once, under its `dedupeKey`\n * (so `/a` and `/a/` are one page), keeping the spelling that arrived first.\n * Pages run one at a time so a site sees one request at a time.\n *\n * A page that redirects in the browser is not a page the run checks. Its\n * destination joins the frontier in its place, under the same rules.\n */\nimport type { AnalyzeOptions, PageAnalyzer } from \"./analyzer.js\";\nimport { A11yError, type PageResult, type ProgressListener, type Redirect } from \"../types.js\";\nimport type { ExcludeFilter, ExcludeGlob } from \"./exclude.js\";\nimport { dedupeKey, isHttpUrl, isPageLink, normalizeUrl, sameHost } from \"./url.js\";\n\nexport interface CrawlOptions {\n /** Normalized http(s) seeds; each seed's host is in scope. */\n seeds: string[];\n /** `false` = check exactly `seeds`. */\n crawl: boolean;\n /** Hard cap on pages analyzed. Absent means no cap: the whole frontier is analyzed. */\n maxPages?: number;\n /** Extra URLs to enqueue right after the seeds (from the sitemap). */\n extra: string[];\n /**\n * Keeps matching URLs out of the frontier, beside the same-host and\n * asset-extension checks. Absent means nothing is excluded.\n */\n exclude?: ExcludeFilter;\n analyze: AnalyzeOptions;\n /** Told about each step as it happens. Absent means silent. */\n onProgress?: ProgressListener;\n}\n\nexport interface CrawlOutcome {\n /** In visit order. `source` is \"seed\", \"sitemap\" (came from `extra`) or \"link\". */\n pages: Omit<PageResult, \"score\">[];\n /** Distinct pages (by `dedupeKey`) that entered the frontier. */\n discovered: number;\n /** Left in the frontier when `maxPages` stopped the run; `0` without a cap. */\n skipped: number;\n /**\n * Dequeued and dropped unloaded, because the browser had already landed on\n * that page under another spelling.\n * `checked + skipped + duplicates + redirects.length` is `discovered`.\n */\n duplicates: number;\n /** Pages that redirected in the browser, in visit order. Never in `pages`. */\n redirects: Redirect[];\n /**\n * Distinct URLs (by `dedupeKey`) an `exclude` pattern kept out. Never part\n * of `discovered`: an excluded URL is dropped where an off-host URL and a\n * `.png` link are, before the frontier.\n */\n excluded: number;\n}\n\ntype Source = PageResult[\"source\"];\n\n/** What `enqueue` did with a URL, and the pattern when one excluded it. */\ntype Enqueued =\n | { as: \"queued\" | \"seen\" | \"invalid\" | \"asset\" | \"off-host\" }\n | { as: \"excluded\"; pattern: ExcludeGlob };\n\ninterface Candidate {\n url: string;\n key: string;\n source: Source;\n}\n\n/**\n * BFS over a FIFO frontier: seeds, then `extra`, then links in the order each\n * page listed them. Every candidate goes through `normalizeUrl`, and is\n * enqueued once per `dedupeKey` (so a trailing slash does not make a second\n * page), only when `isPageLink` and `sameHost` with **any** seed. The first\n * spelling seen is the one fetched and reported. Pages run sequentially.\n * - A seed that fails to analyze rethrows (nothing was checked: exit 2).\n * - Any other page's failure is recorded as `error` and the crawl continues.\n * - A page's `finalUrl` (where the browser landed) counts as visited, so a\n * queued spelling of it is dropped at dequeue time (`duplicates`), and a\n * link to it seen afterwards is never queued.\n * - With `maxPages` set, stops when `pages.length === maxPages`; `skipped` =\n * frontier left behind. Without it, runs the frontier dry and `skipped` is 0.\n * - With `crawl: false`, `extra` is ignored and no links are followed.\n * - With `exclude` set, a URL whose path a pattern matches is never enqueued,\n * never fetched and never counted in `discovered`; `excluded` counts those,\n * once per key. `onProgress` hears one `excluded` event before `browser`\n * when the count is not zero.\n * - A page that redirects in the browser is recorded in `redirects`, not in\n * `pages`, and does not count toward `maxPages`. Its destination is\n * enqueued with the page's own `source`, under `crawl: false` too, since it\n * is the same page under a new address. A seed whose destination is not\n * http(s), is on another host, or is excluded, rethrows: otherwise nothing\n * would be checked.\n * - `onProgress` hears `browser` once before the first analyze (that is where\n * the lazy launch happens), `page` before each analyze, `checked` (or\n * `redirected`) after each, and `done` at the end. A failing seed rethrows\n * before `checked`.\n */\nexport async function crawl(opts: CrawlOptions, analyzer: PageAnalyzer): Promise<CrawlOutcome> {\n const frontier: Candidate[] = [];\n /** Keys that entered the frontier, or that the browser landed on. */\n const seen = new Set<string>();\n /** Keys analyzed, or that the browser landed on. */\n const visited = new Set<string>();\n /** Keys an `exclude` pattern kept out, so a page linked thirty times counts once. */\n const excluded = new Set<string>();\n const progress = opts.onProgress ?? (() => undefined);\n\n const enqueue = (raw: string, source: Source): Enqueued => {\n let url: string;\n let key: string;\n try {\n url = normalizeUrl(raw);\n key = dedupeKey(url);\n } catch {\n return { as: \"invalid\" };\n }\n // A seed is what the user asked for, so it is never filtered as an asset;\n // it still has to be on its own host, which it is by definition.\n if (source !== \"seed\" && !isPageLink(url)) return { as: \"asset\" };\n if (!opts.seeds.some((seed) => sameHost(seed, url))) return { as: \"off-host\" };\n // After the scope checks, so an off-host or asset URL is not also counted\n // as excluded; the two filters answer different questions and a URL that\n // was never in scope was not kept out by a pattern.\n const pattern = opts.exclude?.match(url) ?? null;\n if (pattern !== null) {\n excluded.add(key);\n return { as: \"excluded\", pattern };\n }\n if (seen.has(key)) return { as: \"seen\" };\n seen.add(key);\n frontier.push({ url, key, source });\n return { as: \"queued\" };\n };\n\n /** Where the browser landed is a page the run has now checked, whatever it was asked for. */\n const landed = (finalUrl: string | undefined): void => {\n if (finalUrl === undefined) return;\n try {\n const key = dedupeKey(finalUrl);\n seen.add(key);\n visited.add(key);\n } catch {\n // Not a URL the crawl could ever queue, so nothing to mark.\n }\n };\n\n for (const seed of opts.seeds) enqueue(seed, \"seed\");\n if (opts.crawl) for (const url of opts.extra) enqueue(url, \"sitemap\");\n // Discovery has settled as far as it can before a page is fetched, and this\n // is where a pattern that matched far more than intended is worth seeing:\n // the browser has not launched yet, so the run is still cheap to stop.\n if (excluded.size > 0) {\n progress({ kind: \"excluded\", urls: excluded.size, patterns: opts.exclude?.patterns.length ?? 0 });\n }\n\n const pages: CrawlOutcome[\"pages\"] = [];\n const redirects: Redirect[] = [];\n let duplicates = 0;\n const capped = (): boolean => opts.maxPages !== undefined && pages.length >= opts.maxPages;\n for (let next = 0; next < frontier.length && !capped(); next++) {\n const candidate = frontier[next];\n if (candidate === undefined) break;\n const { url, key, source } = candidate;\n if (visited.has(key)) {\n duplicates += 1;\n continue;\n }\n visited.add(key);\n const index = pages.length + 1;\n // Not `index === 1`: a redirect pushes no page, so the next one reuses that index.\n if (pages.length + redirects.length === 0) progress({ kind: \"browser\" });\n progress({ kind: \"page\", index, queued: frontier.length, url });\n try {\n const analyzed = await analyzer.analyze(url, opts.analyze);\n if (\"redirect\" in analyzed) {\n const to = normalized(analyzed.redirect);\n // Recorded and narrated before the seed checks below on purpose: the\n // `↪` line then precedes the fatal message that explains it, and a\n // throw discards `redirects` anyway.\n redirects.push({ url, to, source });\n progress({ kind: \"redirected\", index, url, to });\n const outcome = enqueue(to, source);\n if (source === \"seed\") {\n if (!isHttpUrl(to)) {\n throw new A11yError(`${url} redirects to ${to}, which is not an http(s) URL.`);\n }\n if (outcome.as === \"off-host\") {\n throw new A11yError(`${url} redirects to ${to}, which is on another host. Check ${to} instead.`);\n }\n if (outcome.as === \"excluded\") {\n throw new A11yError(`${outcome.pattern.source} excludes ${to}, where the seed ${url} redirects.`);\n }\n }\n continue;\n }\n pages.push({ ...analyzed.result, url, source });\n landed(analyzed.finalUrl);\n if (opts.crawl) for (const link of analyzed.links) enqueue(link, \"link\");\n progress({ kind: \"checked\", index, url, violations: analyzed.result.violations.length });\n } catch (err) {\n if (source === \"seed\") throw err;\n const error = err instanceof Error ? err.message : String(err);\n pages.push({ url, source, violations: [], passes: 0, incomplete: 0, error });\n progress({ kind: \"checked\", index, url, violations: 0, error });\n }\n }\n\n const skipped = frontier.length - pages.length - duplicates - redirects.length;\n progress({ kind: \"done\", checked: pages.length, skipped });\n return {\n pages,\n discovered: frontier.length,\n skipped,\n duplicates,\n redirects,\n excluded: excluded.size,\n };\n}\n\n/** `normalizeUrl`, or the string as it came when it is not a URL. */\nfunction normalized(url: string): string {\n try {\n return normalizeUrl(url);\n } catch {\n return url;\n }\n}\n","/**\n * Keeping part of a site out of the crawl (proposal 0059).\n *\n * A pattern is matched against the URL's **path** alone, never the scheme,\n * host, port, query or fragment. The host is exactly the part that changes\n * between a local preview and production, so one pattern has to survive the\n * move: `/proposals/**` reads the same against `http://127.0.0.1:4321/` and\n * against `https://example.com/`.\n *\n * A trailing slash is not part of the comparison, because a crawl produces\n * both spellings — a sitemap usually lists `/proposals/`, a bare link usually\n * points at `/proposals`. Stripping it means a pattern cannot catch one and\n * silently miss the other. Stripping it is also what makes `/proposals/**`\n * cover the section index as well as the pages under it, since picomatch's\n * `**` already matches zero segments.\n *\n * Matching itself is the family's file-glob matcher, so an exclusion and a\n * collection's `paths:` read a `*`, a `**` and a dot segment the same way. A\n * URL path never carries a raw backslash — the WHATWG parser rewrites one and\n * percent-encodes the rest — so that matcher's separator normalization cannot\n * change what a pattern here means.\n */\nimport { matchesFileGlob } from \"../../shared/globs.js\";\n\n/**\n * One pattern, and the name a message gives it.\n *\n * A pattern reaches a run from one of two places, and an error about it has to\n * say which. `--exclude \"/proposals/**\"` for a typed flag, and the config\n * file's own shape for a key, so that a run whose patterns came from\n * `a11y.exclude:` never names a flag the user did not type.\n */\nexport interface ExcludeGlob {\n glob: string;\n /** How a message names this pattern: a flag, or a file and a key. */\n source: string;\n}\n\nexport interface ExcludeFilter {\n /** The patterns, in the order they were given. */\n readonly patterns: readonly ExcludeGlob[];\n /** The first pattern that keeps `url` out of the crawl, or `null`. */\n match(url: string): ExcludeGlob | null;\n /** Whether any pattern keeps `url` out of the crawl. */\n matches(url: string): boolean;\n}\n\n/**\n * The tail every \"that is not a path glob\" message ends with, so the flag and\n * the config key say the same thing about the same mistake.\n */\nexport const MUST_START_WITH_SLASH = 'must start with \"/\": it matches a URL path.';\n\n/**\n * Can this pattern ever match a path? A path always starts with `/`, so a\n * pattern that does not is a silent near-miss rather than a filter, and both\n * the flag and the config key refuse it.\n */\nexport function isUrlPathGlob(pattern: string): boolean {\n return pattern.startsWith(\"/\");\n}\n\n/**\n * The path a pattern is matched against: the URL's `pathname` with one\n * trailing `/` dropped, unless the path is exactly `/`. `null` for a string\n * that is not a URL, which the crawl has already dropped for other reasons.\n */\nexport function excludePath(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n const path = parsed.pathname;\n return path.length > 1 && path.endsWith(\"/\") ? path.slice(0, -1) : path;\n}\n\n/**\n * A filter over `patterns`, in order. An empty list excludes nothing, which is\n * the default and the shape a run with neither the flag nor the config key\n * has. Patterns are taken as given: one value is one pattern, and a comma\n * inside it is a literal comma, per 0034's one-separator rule.\n */\nexport function createExcludeFilter(patterns: readonly ExcludeGlob[]): ExcludeFilter {\n const list = [...patterns];\n const match = (url: string): ExcludeGlob | null => {\n if (list.length === 0) return null;\n const path = excludePath(url);\n if (path === null) return null;\n return list.find((pattern) => matchesFileGlob(path, pattern.glob)) ?? null;\n };\n return {\n patterns: list,\n match,\n matches: (url) => match(url) !== null,\n };\n}\n","/**\n * Where a check starts from.\n *\n * A collection may declare `url:`, the site root its documents are published\n * at (proposal 0041, rule 12), which makes \"check the guides\" something a\n * person can mean. Four sources can therefore name a seed, and this module is\n * the one place that decides between them: first non-empty wins, in the order\n * positional URLs, the collections named by `--collection`, `a11y.urls`, then\n * every declared collection that has a `url`.\n *\n * `a11y.urls` keeps its place ahead of the collections because a site has\n * entry points no documentation collection covers, and because nothing that\n * ships today should have to move.\n *\n * It is separate from the CLI, and pure, because the interesting part is the\n * order and the three refusals, and neither is worth a spawned bin with a\n * browser behind it to test.\n */\nimport { selectCollections, type CollectionConfig } from \"../../shared/collections.js\";\nimport { A11yError } from \"../types.js\";\n\n/**\n * Nothing to check. Also thrown by `runCheck` for a programmatic caller that\n * resolved its own seeds, which is why the text lives here and not there.\n */\nexport const NO_SEEDS_MESSAGE =\n \"No URLs to check. Pass one or more, set url: on a collection, or set a11y.urls in manni.config.yaml.\";\n\n/** The family file's name, for a message about a config that was never found. */\nconst CONFIG_NAME = \"manni.config.yaml\";\n\nexport interface SeedSources {\n /** Positional `[urls...]`, as typed. Validated and normalized by `runCheck`. */\n urls: readonly string[];\n /** `--collection` names, one per occurrence. Empty means none were named. */\n collections: readonly string[];\n /** `a11y.urls`, when the key is set. An empty list counts as unset. */\n configUrls?: readonly string[];\n /** Every collection the family file declares, in declaration order. */\n declared: readonly CollectionConfig[];\n /**\n * The config file as the user would name it, for the unknown-name message.\n * `null` or absent (no file found) names the canonical family file, since\n * that is where the missing `collections:` would go.\n */\n source?: string | null;\n}\n\n/**\n * The seeds a run checks, or an `A11yError` explaining why there are none.\n *\n * 1. `urls` when non-empty.\n * 2. The `url` of each collection named by `collections`, in **declaration**\n * order, repeats and shared urls collapsed. A named collection with no\n * `url` is an error, and so is a name that is not declared.\n * 3. `configUrls` when non-empty, exactly as configured.\n * 4. The `url` of every declared collection that has one, in declaration\n * order, shared urls collapsed. One without a `url` contributes nothing.\n *\n * Naming a url-less collection fails where leaving it out of the default set\n * does not: `--collection blog` asked for something specific and has to be\n * told it cannot be done, while a bare `manni a11y check` gets whatever is\n * publishable.\n */\nexport function resolveSeeds(sources: SeedSources): string[] {\n const { urls, collections: names, declared } = sources;\n\n if (names.length > 0) {\n // The shape of the invocation, before anything about the config: a run\n // that named both a collection and a URL has no reading to pick between.\n if (urls.length > 0) {\n throw new A11yError(\n \"--collection selects a configured collection; it cannot be combined with URLs.\",\n );\n }\n // No config at all — nothing was found, or `--no-config` refused it. The\n // flag selects from a config file, so say that rather than reporting the\n // name as unknown: \"Configured: (none).\" blames the name for the absence\n // of the file. `manni meta` refuses the same combination with the same\n // sentence, which is the parity CLAUDE.md asks for on a shared flag. A\n // real config always carries a source, so this cannot mistake a\n // programmatic caller that passed collections without one.\n if (sources.source == null && declared.length === 0) {\n throw new A11yError(\"--collection needs a config file to select from.\");\n }\n const selected = selectCollections(\n declared,\n names,\n sources.source ?? CONFIG_NAME,\n (message) => new A11yError(message),\n );\n const seeds: string[] = [];\n for (const collection of selected) {\n if (collection.url === undefined) {\n throw new A11yError(`collection \"${collection.name}\" has no url: to check.`);\n }\n seeds.push(collection.url);\n }\n return unique(seeds);\n }\n\n if (urls.length > 0) return [...urls];\n\n const configUrls = sources.configUrls ?? [];\n if (configUrls.length > 0) return [...configUrls];\n\n const published = unique(\n declared.flatMap((collection) => (collection.url === undefined ? [] : [collection.url])),\n );\n if (published.length > 0) return published;\n\n throw new A11yError(NO_SEEDS_MESSAGE);\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n","/**\n * Sitemap discovery: the page list a site publishes for itself.\n *\n * A sitemap reaches pages no link points at, so the crawl reads it first and\n * follows links second. Nothing here is allowed to fail the run: a site with\n * no sitemap, a broken one, or one behind a firewall is a site to crawl by\n * links alone, and the outcome says so with `source: null`.\n */\nimport { DOMParser } from \"@xmldom/xmldom\";\nimport { isHttpUrl, isPageLink, normalizeUrl, sameHost } from \"./url.js\";\n\n/** `fetch` narrowed to what this module needs, so tests inject a fake. */\nexport type Fetcher = (\n url: string,\n) => Promise<{ ok: boolean; status: number; text(): Promise<string> }>;\n\nexport interface SitemapDiscovery {\n /** The sitemap (or sitemap index) URL that was read, `null` when none was usable. */\n source: string | null;\n /** Same-host page URLs from every `<loc>`, normalized and deduped, in document order. */\n urls: string[];\n}\n\nexport interface ParsedSitemap {\n kind: \"index\" | \"urlset\";\n locs: string[];\n}\n\n/** How many levels of `<sitemapindex>` nesting are followed below the first. */\nconst MAX_INDEX_DEPTH = 3;\n/** How many child sitemaps one discovery reads, across every index it meets. */\nconst MAX_CHILD_SITEMAPS = 50;\n/** A robots.txt or sitemap that has not answered in this long is \"no sitemap\". */\nconst FETCH_TIMEOUT_MS = 10_000;\n\nconst defaultFetcher: Fetcher = (url) =>\n fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n\n/**\n * The file names tried in each directory of the seed's path, in this order.\n * `sitemap.xml` is the convention; `sitemap-index.xml` is what Astro (and so\n * Starlight) emits; `sitemap_index.xml` is Yoast's and Hugo's spelling.\n */\nconst WELL_KNOWN_SITEMAP_FILES = [\"sitemap.xml\", \"sitemap-index.xml\", \"sitemap_index.xml\"];\n\n/**\n * Candidates, in order:\n * 1. GET `<origin>/robots.txt`; every `Sitemap:` line (case-insensitive key,\n * absolute URL) is a candidate, in order.\n * 2. Then, for each directory of the seed's path from the seed's own\n * directory up to `/`, nearest first (`https://h/manni/meta/fix/` walks\n * `/manni/meta/fix/`, `/manni/meta/`, `/manni/`, `/`; a seed that names a\n * file, `https://h/manni/page.html`, starts at `/manni/`), the files\n * `sitemap.xml`, `sitemap-index.xml`, `sitemap_index.xml`, in that order\n * within each directory. A project site under a path prefix (GitHub Pages)\n * cannot write to the origin's `/robots.txt` or `/sitemap.xml`, which is\n * why the walk starts at the seed and not at the root.\n * Candidates are deduped, keeping the first position of each.\n *\n * Then parse the first candidate that returns 2xx with `@xmldom/xmldom`\n * (`DOMParser`, errors swallowed); a 2xx body that is not a sitemap is skipped\n * and the next candidate tried. A `<sitemapindex>` recurses into each child\n * `<sitemap><loc>` (depth ≤ 3, ≤ 50 child sitemaps). A `<urlset>` yields its\n * `<url><loc>` values. Keep only `sameHost(seed, loc) && isPageLink(loc)`.\n * `source` is the candidate that was actually used.\n *\n * A network error, non-2xx, or unparseable body means \"no sitemap here\" —\n * never an error, and never a page failure. `.xml.gz` bodies are not\n * decompressed (deferred).\n */\nexport async function discoverSitemap(\n seed: string,\n fetcher: Fetcher = defaultFetcher,\n): Promise<SitemapDiscovery> {\n const origin = new URL(seed).origin;\n const candidates = await robotsSitemaps(`${origin}/robots.txt`, fetcher);\n for (const candidate of wellKnownCandidates(seed)) {\n if (!candidates.includes(candidate)) candidates.push(candidate);\n }\n\n for (const candidate of candidates) {\n const parsed = await fetchSitemap(candidate, fetcher);\n if (parsed === null) continue;\n const walk: Walk = { fetcher, seen: new Set([candidate]), childBudget: MAX_CHILD_SITEMAPS };\n const locs = await expand(parsed, 0, walk);\n return { source: candidate, urls: pageUrls(seed, locs) };\n }\n return { source: null, urls: [] };\n}\n\n/** Pure parser, exported for tests: returns `{ kind: \"index\" | \"urlset\", locs: string[] }` or `null`. */\nexport function parseSitemapXml(text: string): ParsedSitemap | null {\n if (text.trim() === \"\") return null;\n let root: XmlNode | null;\n try {\n // Every parse problem is routed to `onError` and ignored; what matters is\n // whether a sitemap root came out the other side.\n const doc = new DOMParser({ onError: () => undefined }).parseFromString(text, \"text/xml\");\n root = doc.documentElement;\n } catch {\n return null;\n }\n if (root === null) return null;\n const kind = root.localName;\n if (kind !== \"sitemapindex\" && kind !== \"urlset\") return null;\n const entry = kind === \"sitemapindex\" ? \"sitemap\" : \"url\";\n const locs: string[] = [];\n for (const child of childElements(root, entry)) {\n for (const loc of childElements(child, \"loc\")) {\n const value = (loc.textContent ?? \"\").trim();\n if (value !== \"\") locs.push(value);\n }\n }\n return { kind: kind === \"sitemapindex\" ? \"index\" : \"urlset\", locs };\n}\n\ntype XmlDocument = ReturnType<DOMParser[\"parseFromString\"]>;\n/**\n * xmldom's `Node`, reached through the parser's own types rather than an\n * import so the alias tracks whatever the installed version declares. It is\n * enough here: `localName`, `textContent` and the sibling walk are all on\n * `Node`, and `documentElement` narrows to it.\n */\ntype XmlNode = NonNullable<XmlDocument[\"firstChild\"]>;\n\n/** Element children of `node` whose local name is `name`, in document order. */\nfunction childElements(node: XmlNode, name: string): XmlNode[] {\n const out: XmlNode[] = [];\n for (let n = node.firstChild; n !== null; n = n.nextSibling) {\n if (n.nodeType === 1 && n.localName === name) out.push(n);\n }\n return out;\n}\n\ninterface Walk {\n fetcher: Fetcher;\n /** Sitemap URLs already read, so an index that lists itself terminates. */\n seen: Set<string>;\n /** Child sitemaps still allowed; shared across the whole recursion. */\n childBudget: number;\n}\n\nasync function expand(parsed: ParsedSitemap, depth: number, walk: Walk): Promise<string[]> {\n if (parsed.kind === \"urlset\") return parsed.locs;\n if (depth >= MAX_INDEX_DEPTH) return [];\n const out: string[] = [];\n for (const child of parsed.locs) {\n if (walk.childBudget <= 0) break;\n if (!isHttpUrl(child) || walk.seen.has(child)) continue;\n walk.seen.add(child);\n walk.childBudget -= 1;\n const nested = await fetchSitemap(child, walk.fetcher);\n if (nested === null) continue;\n out.push(...(await expand(nested, depth + 1, walk)));\n }\n return out;\n}\n\n/**\n * The well-known sitemap file names in every directory of the seed's path,\n * nearest directory first, root last. Query and fragment are dropped; the\n * directory of `/manni/page.html` is `/manni/`, of `/manni/` is itself.\n */\nfunction wellKnownCandidates(seed: string): string[] {\n const url = new URL(seed);\n const directories = ancestorDirectories(url.pathname);\n return directories.flatMap((dir) =>\n WELL_KNOWN_SITEMAP_FILES.map((file) => `${url.origin}${dir}${file}`),\n );\n}\n\n/**\n * `/manni/meta/fix/` → `[\"/manni/meta/fix/\", \"/manni/meta/\", \"/manni/\", \"/\"]`;\n * `/manni/page.html` → `[\"/manni/\", \"/\"]`; `/` → `[\"/\"]`.\n */\nfunction ancestorDirectories(pathname: string): string[] {\n let dir = pathname.slice(0, pathname.lastIndexOf(\"/\") + 1);\n const out: string[] = [];\n for (;;) {\n out.push(dir);\n if (dir === \"/\") return out;\n // Drop the trailing slash, then everything after the previous one.\n const parent = dir.slice(0, dir.lastIndexOf(\"/\", dir.length - 2) + 1);\n dir = parent === \"\" ? \"/\" : parent;\n }\n}\n\n/** The `Sitemap:` candidates from a robots.txt, or none when it is missing. */\nasync function robotsSitemaps(url: string, fetcher: Fetcher): Promise<string[]> {\n const text = await fetchText(url, fetcher);\n if (text === null) return [];\n const out: string[] = [];\n for (const line of text.split(/\\r?\\n/)) {\n const match = /^\\s*sitemap\\s*:\\s*(\\S+)/i.exec(line);\n const candidate = match?.[1];\n if (candidate !== undefined && isHttpUrl(candidate) && !out.includes(candidate)) {\n out.push(candidate);\n }\n }\n return out;\n}\n\nasync function fetchSitemap(url: string, fetcher: Fetcher): Promise<ParsedSitemap | null> {\n const text = await fetchText(url, fetcher);\n return text === null ? null : parseSitemapXml(text);\n}\n\n/** The body of a 2xx response, or `null` for anything else, thrown included. */\nasync function fetchText(url: string, fetcher: Fetcher): Promise<string | null> {\n try {\n const response = await fetcher(url);\n if (!response.ok) return null;\n return await response.text();\n } catch {\n return null;\n }\n}\n\n/** Filter, normalize and dedupe the `<loc>` values into crawlable page URLs. */\nfunction pageUrls(seed: string, locs: string[]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const loc of locs) {\n if (!isHttpUrl(loc) || !sameHost(seed, loc) || !isPageLink(loc)) continue;\n const url = normalizeUrl(loc);\n if (seen.has(url)) continue;\n seen.add(url);\n out.push(url);\n }\n return out;\n}\n","/**\n * `manni a11y check`, the command core.\n *\n * Validates the seeds, finds the sitemap, runs the crawl, drops the findings\n * below the severity floor, scores each page and totals the summary. No CLI\n * and no IO of its own beyond the injected analyzer and fetcher, so it can be\n * tested end to end with neither a browser nor a network.\n */\nimport type { PageAnalyzer } from \"../core/analyzer.js\";\nimport { crawl, type CrawlOutcome } from \"../core/crawl.js\";\nimport { createExcludeFilter, type ExcludeGlob } from \"../core/exclude.js\";\nimport { NO_SEEDS_MESSAGE } from \"../core/seeds.js\";\nimport { discoverSitemap, type Fetcher, type SitemapDiscovery } from \"../core/sitemap.js\";\nimport { isHttpUrl, normalizeUrl } from \"../core/url.js\";\nimport {\n A11yError,\n meetsSeverity,\n type CheckRun,\n type CheckSummary,\n type Severity,\n type PageResult,\n type ProgressListener,\n} from \"../types.js\";\n\nexport interface CheckOptions {\n /** Seeds as typed (validated and normalized here). */\n urls: string[];\n /** Follow the sitemap and same-host links. Default `true`. */\n crawl: boolean;\n /** Stop after this many pages. Absent means no cap: every discovered page is checked. */\n maxPages?: number;\n /** axe tags to restrict to; empty is axe's default rule set. Default `[]`. */\n tags: string[];\n /** Minimum family severity reported and counted. Default `\"notice\"`, everything. */\n severity: Severity;\n /** Per-page navigation timeout in ms. Default `30000`. */\n timeout: number;\n /**\n * Globs matched against a URL's path; a match keeps the page out of the\n * crawl. Default `[]`, which excludes nothing. A pattern that excludes a\n * seed is an error, since the seed and the pattern are both the caller's\n * statement and a contradiction is not this command's to resolve. Each\n * pattern carries the name the refusal gives it, so the message points at\n * the flag or the config key the pattern actually came from.\n */\n exclude: ExcludeGlob[];\n}\n\nexport interface CheckDeps {\n analyzer: PageAnalyzer;\n /** Defaults to global `fetch`. */\n fetcher?: Fetcher;\n /**\n * Told about each step as it happens: `sitemap` once discovery is done\n * (only when crawling), then everything the crawl reports. Absent means\n * silent.\n */\n onProgress?: ProgressListener;\n}\n\n/**\n * `maxPages` is deliberately absent: the default is no cap. A capped crawl\n * reports a partial site as checked, so the cap is opt-in, through\n * `--max-pages` or the config key.\n */\nexport const CHECK_DEFAULTS: Readonly<Omit<CheckOptions, \"urls\">> = Object.freeze({\n crawl: true,\n tags: [],\n severity: \"notice\",\n timeout: 30000,\n exclude: [],\n});\n\n/**\n * 1. `urls.length === 0` → A11yError(NO_SEEDS_MESSAGE). The CLI resolves its\n * seeds with `resolveSeeds` and never arrives here empty; a programmatic\n * caller that passed none hears the same thing.\n * 2. Any non-http(s) url → A11yError(`Not an http(s) URL: \"<url>\".`)\n * 3. Any seed an `exclude` pattern matches → A11yError naming **every** such\n * seed, one per line, each under the pattern's own `source`. A user who\n * typed two wrong patterns should not need one run per fix, and nothing is\n * checked either way.\n * 4. When `crawl`: `discoverSitemap(firstSeed)` (one sitemap per run, the first seed's origin).\n * 5. `crawl(...)`, then per page: drop violations below `severity`, compute `score`.\n * 6. Build `CheckSummary`. Always awaits `analyzer.close()` in `finally`.\n */\nexport async function runCheck(opts: CheckOptions, deps: CheckDeps): Promise<CheckRun> {\n const { analyzer } = deps;\n try {\n if (opts.urls.length === 0) {\n throw new A11yError(NO_SEEDS_MESSAGE);\n }\n for (const url of opts.urls) {\n if (!isHttpUrl(url)) throw new A11yError(`Not an http(s) URL: \"${url}\".`);\n }\n const seeds = opts.urls.map(normalizeUrl);\n\n const exclude = createExcludeFilter(opts.exclude);\n const contradictions = seeds\n .map((seed) => {\n const pattern = exclude.match(seed);\n return pattern === null ? null : `${pattern.source} excludes the seed ${seed}.`;\n })\n .filter((line): line is string => line !== null);\n if (contradictions.length > 0) throw new A11yError(contradictions.join(\"\\n\"));\n\n let sitemap: SitemapDiscovery = { source: null, urls: [] };\n const first = seeds[0];\n if (opts.crawl && first !== undefined) {\n sitemap = await discoverSitemap(first, deps.fetcher);\n deps.onProgress?.({ kind: \"sitemap\", source: sitemap.source, urls: sitemap.urls.length });\n }\n\n const outcome = await crawl(\n {\n seeds,\n crawl: opts.crawl,\n maxPages: opts.maxPages,\n extra: sitemap.urls,\n exclude,\n analyze: { tags: opts.tags, timeout: opts.timeout },\n onProgress: deps.onProgress,\n },\n analyzer,\n );\n\n const results = outcome.pages.map((page) => finishPage(page, opts.severity));\n return {\n results,\n redirects: outcome.redirects,\n summary: summarize(results, outcome, sitemap, opts.crawl),\n };\n } finally {\n await analyzer.close();\n }\n}\n\n/** Apply the severity floor, then score what is left. */\nfunction finishPage(page: Omit<PageResult, \"score\">, floor: Severity): PageResult {\n if (page.error !== undefined) return { ...page, violations: [], score: null };\n const violations = page.violations.filter((v) => meetsSeverity(v.severity, floor));\n const applicable = page.passes + violations.length;\n const score = applicable === 0 ? null : Math.round((100 * page.passes) / applicable);\n return { ...page, violations, score };\n}\n\n/**\n * Does this page fail the run?\n *\n * The level is not read here, and that is the decision rather than an\n * oversight. `--severity` is the floor, and the floor is the gate: anything\n * `finishPage` kept fails the page, whatever its level. `manni meta validate`\n * and `manni cite check` fail on error-severity findings alone, because a\n * user decides there what becomes an error, through the schema or through\n * `cite.severity.<rule>`. axe assigns a11y's severities and nothing in the\n * config moves one, so the floor is the only lever this domain's shape\n * allows. Proposal 0064 records why, and supersedes the one paragraph of\n * 0035 that described the two as separable.\n */\nfunction fails(page: PageResult): boolean {\n return page.error !== undefined || page.violations.length > 0;\n}\n\nfunction summarize(\n results: PageResult[],\n { discovered, skipped, duplicates, redirects, excluded }: CrawlOutcome,\n sitemap: SitemapDiscovery,\n crawl: boolean,\n): CheckSummary {\n // Spelled out so the compiler, not a test, says when the family scale moves.\n const bySeverity: Record<Severity, number> = { notice: 0, warning: 0, error: 0 };\n let failed = 0;\n let violations = 0;\n for (const page of results) {\n if (fails(page)) failed += 1;\n for (const violation of page.violations) {\n violations += 1;\n bySeverity[violation.severity] += 1;\n }\n }\n return {\n discovered,\n checked: results.length,\n skipped,\n duplicates,\n redirects: redirects.length,\n excluded,\n failed,\n violations,\n bySeverity,\n sitemap: sitemap.source,\n // The count the progress line already reported as `(N pages)`, so the\n // report cannot disagree with the narration that preceded it.\n sitemapPages: sitemap.urls.length,\n crawl,\n };\n}\n","/**\n * The browser seam.\n *\n * Everything that needs a real page — navigation, the live DOM's links, axe\n * itself — sits behind `PageAnalyzer`, so the crawl and the check core can be\n * exercised with a canned analyzer and no browser. The Playwright-backed\n * implementation lives here too, behind the same interface.\n */\nimport { AxeBuilder } from \"@axe-core/playwright\";\nimport {\n chromium,\n type Browser,\n type BrowserContext,\n type Page,\n type Request,\n type Response,\n} from \"playwright-core\";\nimport {\n A11yError,\n isAxeImpact,\n type AxeImpact,\n type PageResult,\n type Severity,\n type Violation,\n type ViolationNode,\n} from \"../types.js\";\nimport { dedupeKey } from \"./url.js\";\n\nexport interface AnalyzeOptions {\n /** axe tags to restrict to; empty = axe defaults. */\n tags: string[];\n /** Navigation timeout in ms. */\n timeout: number;\n}\n\nexport interface AnalyzedPage {\n /** Raw result: every violation regardless of severity; `source` and `score` are filled in by `runCheck`. */\n result: Omit<PageResult, \"source\" | \"score\">;\n /** Absolute `href` of every `a[href]` in the live DOM after load, unfiltered. */\n links: string[];\n /**\n * The browser's URL once navigation and any redirects settled. The crawl\n * marks it visited so a later queued spelling of the same page is not\n * loaded again. Absent when the analyzer does not know it.\n */\n finalUrl?: string;\n}\n\n/**\n * A page that sent the browser elsewhere once it had loaded: a meta refresh\n * to another page, or a navigation while links or axe were being read. It was\n * not analyzed. Tell it from `AnalyzedPage` with `\"redirect\" in x`.\n */\nexport interface RedirectedPage {\n /** The absolute URL the page redirects to. */\n redirect: string;\n}\n\nexport interface PageAnalyzer {\n /**\n * Load `url` and run axe, or report where the page redirects in the\n * browser. Rejects with `A11yError` on navigation failure or timeout\n * (message names the URL and the underlying reason); the caller decides\n * whether that is fatal (seed) or a per-page `error` (crawled page).\n */\n analyze(url: string, opts: AnalyzeOptions): Promise<AnalyzedPage | RedirectedPage>;\n /** Close the browser. Safe to call when nothing was launched, and twice. */\n close(): Promise<void>;\n}\n\nexport type BrowserChannel = \"chrome\" | \"msedge\" | \"chromium\";\n\n/** The message when nothing launches; the same words the CLI's ladder promises. */\nexport const NO_BROWSER_MESSAGE =\n \"No browser found. Install one with `npx playwright install chromium`, or install Google Chrome or Microsoft Edge.\";\n\n/** The launch order: what the machine already has first, Playwright's own download last. */\nconst CHANNELS: readonly BrowserChannel[] = [\"chrome\", \"msedge\", \"chromium\"];\n\nfunction launch(channel: BrowserChannel): Promise<Browser> {\n return channel === \"chromium\"\n ? chromium.launch({ headless: true })\n : chromium.launch({ channel, headless: true });\n}\n\n/** The first channel that launches, still open, or `null` when none does. */\nasync function launchAny(): Promise<{ browser: Browser; channel: BrowserChannel } | null> {\n for (const channel of CHANNELS) {\n try {\n return { browser: await launch(channel), channel };\n } catch {\n // Not installed, or not launchable here. Try the next one.\n }\n }\n return null;\n}\n\n/**\n * Try, in order: `chromium.launch({ channel: \"chrome\", headless: true })`,\n * `{ channel: \"msedge\" }`, `chromium.launch()` (Playwright's own download).\n * Returns the first that launches, closed again, or `null`. Exported so the\n * integration suite can `describe.skipIf`.\n */\nexport async function findBrowser(): Promise<BrowserChannel | null> {\n const found = await launchAny();\n if (found === null) return null;\n await found.browser.close();\n return found.channel;\n}\n\n/** axe's result shapes, as `@axe-core/playwright` declares them; no direct axe-core import. */\ntype AxeResults = Awaited<ReturnType<AxeBuilder[\"analyze\"]>>;\ntype AxeResult = AxeResults[\"violations\"][number];\ntype AxeNode = AxeResult[\"nodes\"][number];\n\ninterface Session {\n browser: Browser;\n context: BrowserContext;\n}\n\n/**\n * Playwright-backed analyzer. Lazy: the browser launches on the first\n * `analyze`, and one context is reused for every URL. Each URL gets a page\n * of its own, so a navigation a page starts late (a script redirect after axe\n * returned, or a meta refresh reported without waiting for it) stays in that\n * page's tab instead of aborting the next load. A page is closed once the\n * next one exists, never before: Chrome can drop a context left with no page,\n * and `newPage` then fails with \"Failed to open a new tab\".\n * - `page.goto(url, { waitUntil: \"load\", timeout })`; `finalUrl` is `page.url()` after it\n * - one in-page evaluation reads every `a[href]`'s absolute `href` and the\n * first meta refresh; a refresh to another page (by `dedupeKey`) is a\n * redirect, returned before axe runs\n * - a read or axe that rejects because the page navigated: wait up to\n * `timeout` for the URL to change and return the redirect, else rethrow\n * - `new AxeBuilder({ page })`, `.withTags(tags)` only when `tags.length > 0`, `.analyze()`\n * - map axe `Result` → `Violation` (`severity` from `severityOf(impact)`, `impact` kept as\n * axe's word with `null`/`undefined` → \"minor\"; node `target` joined with \" \";\n * `failureSummary ?? \"\"`), `passes.length`, `incomplete.length`\n * - launch failure on every channel → `A11yError(NO_BROWSER_MESSAGE)`\n */\nexport function createPlaywrightAnalyzer(): PageAnalyzer {\n let session: Promise<Session> | null = null;\n let current: Page | null = null;\n\n const open = (): Promise<Session> => {\n session ??= (async () => {\n const found = await launchAny();\n if (found === null) throw new A11yError(NO_BROWSER_MESSAGE);\n const context = await found.browser.newContext();\n return { browser: found.browser, context };\n })();\n return session;\n };\n\n return {\n async analyze(url, opts) {\n const { context } = await open();\n const page = await context.newPage();\n const previous = current;\n current = page;\n // Its outcome is settled; a failure to close it is not this URL's. Not\n // reentrant: an overlapping call would close a page still being read,\n // so the caller (the crawl, one page at a time) sequences its calls.\n await previous?.close().catch(() => undefined);\n // Every navigation the main frame starts, first hop only, so the one\n // that follows the load can be named even when it never commits (an\n // unresolvable host leaves the browser on its own error page).\n const navigations: Request[] = [];\n const onRequest = (request: Request): void => {\n if (\n request.isNavigationRequest() &&\n request.frame() === page.mainFrame() &&\n request.redirectedFrom() === null\n ) {\n navigations.push(request);\n }\n };\n page.on(\"request\", onRequest);\n try {\n return await analyzePage(page, url, opts, navigations);\n } finally {\n page.off(\"request\", onRequest);\n }\n },\n\n async close() {\n if (session === null) return;\n const pending = session;\n session = null;\n current = null;\n try {\n const { browser } = await pending;\n await browser.close();\n } catch {\n // The launch itself failed: there is nothing to close.\n }\n },\n };\n}\n\n/**\n * One `analyze`, on a page of its own. `navigations` is filled while it\n * runs: the first hop of every main-frame navigation, in order.\n */\nasync function analyzePage(\n page: Page,\n url: string,\n opts: AnalyzeOptions,\n navigations: readonly Request[],\n): Promise<AnalyzedPage | RedirectedPage> {\n let response: Response | null;\n try {\n response = await page.goto(url, { waitUntil: \"load\", timeout: opts.timeout });\n } catch (err) {\n throw new A11yError(`Could not load ${url}: ${reason(err)}`);\n }\n // Where the load settled, HTTP redirects included. The browser being\n // anywhere else later means the page navigated on its own.\n const loaded = response?.url() ?? page.url();\n\n /**\n * The first navigation the main frame started after the load's own, or\n * `undefined` when it started none. The load's own is found by walking its\n * HTTP redirect hops back to the first request. When `goto` gave no\n * response (a service worker answered it), the listener was attached just\n * before `goto`, so its navigation is the first one recorded.\n */\n const nextNavigation = (): Request | undefined => {\n let first = response?.request() ?? null;\n for (let from = first?.redirectedFrom() ?? null; from !== null; from = from.redirectedFrom()) {\n first = from;\n }\n // No response means a service worker answered `goto`: its navigation is [0].\n const own = first === null ? 0 : navigations.indexOf(first);\n return own === -1 ? undefined : navigations[own + 1];\n };\n\n /**\n * Where the page sent the browser. The navigation request is named even\n * when it never commits, since an unresolvable host leaves the frame on the\n * browser's own error page. Without one, the frame's URL is the answer:\n * every caller has already seen it move off `loaded`, and Playwright\n * reports the request before the URL changes, so this is a fallback only.\n */\n const destination = (): string => nextNavigation()?.url() ?? page.url();\n\n /**\n * After a read rejected: when the page started a navigation, wait for the\n * main frame to commit it and return the redirect. When it started none,\n * the error was not a navigation and stands at once, rather than after a\n * second wait of `timeout`.\n */\n const navigatedAway = async (): Promise<RedirectedPage | null> => {\n if (nextNavigation() === undefined && samePage(page.url(), loaded)) return null;\n try {\n await page.waitForURL((u) => !samePage(u.href, loaded), {\n waitUntil: \"commit\",\n timeout: opts.timeout,\n });\n return { redirect: destination() };\n } catch {\n return null;\n }\n };\n\n let read: InPage;\n try {\n read = await page.evaluate(readPage);\n } catch (err) {\n const moved = await navigatedAway();\n if (moved !== null) return moved;\n throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);\n }\n const refresh = refreshTarget(read.refresh, page.url());\n if (refresh !== null) return { redirect: refresh };\n\n let builder = new AxeBuilder({ page });\n if (opts.tags.length > 0) builder = builder.withTags(opts.tags);\n let results: AxeResults;\n try {\n results = await builder.analyze();\n } catch (err) {\n const moved = await navigatedAway();\n if (moved !== null) return moved;\n throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);\n }\n // The page can navigate between two reads without either rejecting, and\n // then what was read belongs to the destination, not to `url`. Where it\n // went comes from the recorded request, with the frame's URL as fallback.\n // A script redirect that fires after axe returns is not seen at all: the\n // page was analyzed as it stood, which is what it was when it was read.\n if (!samePage(page.url(), loaded)) return { redirect: destination() };\n return {\n result: {\n url,\n violations: results.violations.map(toViolation),\n passes: results.passes.length,\n incomplete: results.incomplete.length,\n },\n links: read.links,\n finalUrl: page.url(),\n };\n}\n\n/** What one in-page evaluation reads before axe runs. */\ninterface InPage {\n /** Every `a[href]`'s absolute `href`. */\n links: string[];\n /** The first `<meta http-equiv=\"refresh\">`'s `content`, or `null`. */\n refresh: string | null;\n}\n\n/** Runs in the page. The project has no DOM lib, so the DOM is typed as narrowly as it is used. */\nfunction readPage(): InPage {\n const doc = (\n globalThis as unknown as {\n document: {\n querySelectorAll(selector: string): ArrayLike<{ href: string }>;\n querySelector(selector: string): { getAttribute(name: string): string | null } | null;\n };\n }\n ).document;\n return {\n links: Array.from(doc.querySelectorAll(\"a[href]\"), (a) => a.href),\n refresh:\n doc.querySelector('meta[http-equiv=\"refresh\" i]')?.getAttribute(\"content\") ?? null,\n };\n}\n\n/**\n * The URL part of a meta refresh's `content`: `<delay>;url=<url>` or\n * `<delay>, <url>`, `url=` optional and any case, quotes around the URL\n * optional. `null` for a bare delay, which is a reload rather than a redirect.\n */\nexport function refreshUrl(content: string): string | null {\n // The delay may be fractional (\"0.5\"), hence the dot. A malformed delay\n // such as \"...\" also passes, which costs nothing: only the URL is used.\n const match = /^\\s*[\\d.]*\\s*[;,]\\s*(?:url\\s*=\\s*)?(.*)$/is.exec(content);\n let target = match?.[1]?.trim() ?? \"\";\n const quote = target[0];\n if (quote === \"'\" || quote === '\"') {\n const end = target.indexOf(quote, 1);\n target = (end === -1 ? target.slice(1) : target.slice(1, end)).trim();\n }\n return target === \"\" ? null : target;\n}\n\n/**\n * Where a meta refresh sends the browser, resolved against the page, or\n * `null` when there is no refresh, no URL in it, or it points back at the\n * page itself.\n */\nfunction refreshTarget(content: string | null, base: string): string | null {\n if (content === null) return null;\n const target = refreshUrl(content);\n if (target === null) return null;\n let to: string;\n try {\n to = new URL(target, base).href;\n } catch {\n return null;\n }\n return samePage(to, base) ? null : to;\n}\n\n/** One page by `dedupeKey`; a string that is not an http(s) URL compares as itself. */\nfunction samePage(a: string, b: string): boolean {\n try {\n return dedupeKey(a) === dedupeKey(b);\n } catch {\n return a === b;\n }\n}\n\n/** What axe may put in `impact`; the same union `@axe-core/playwright` declares. */\ntype ImpactValue = AxeResult[\"impact\"];\n\n/**\n * axe's impact onto the family severity scale (`src/shared/severity.ts`).\n * Four levels fold onto three: `critical` and `serious` are both `error`,\n * `moderate` is `warning`, `minor` is `notice`. A missing impact, which axe\n * reports as `null` for a rule with no impact set, is the lowest level. The\n * original word is kept on the violation as `impact`, so nothing is lost for\n * anyone looking the rule up where axe names it.\n */\nexport function severityOf(impact: ImpactValue | null | undefined): Severity {\n switch (impact) {\n case \"critical\":\n case \"serious\":\n return \"error\";\n case \"moderate\":\n return \"warning\";\n default:\n return \"notice\";\n }\n}\n\n/** axe's word as it stands, or `minor` when it gave none. */\nfunction impactOf(impact: ImpactValue | null | undefined): AxeImpact {\n return impact != null && isAxeImpact(impact) ? impact : \"minor\";\n}\n\nfunction toViolation(result: AxeResult): Violation {\n return {\n id: result.id,\n severity: severityOf(result.impact),\n impact: impactOf(result.impact),\n help: result.help,\n helpUrl: result.helpUrl,\n tags: result.tags,\n nodes: result.nodes.map(toNode),\n };\n}\n\nfunction toNode(node: AxeNode): ViolationNode {\n return {\n // A shadow-DOM target is a nested list of selectors; flatten it the same way.\n target: node.target.map((t) => (Array.isArray(t) ? t.join(\" \") : t)).join(\" \"),\n html: node.html,\n summary: node.failureSummary ?? \"\",\n };\n}\n\n/**\n * The first line of a Playwright error. The rest is a call log that repeats\n * the URL and the timeout, which the caller's message already carries.\n */\nfunction reason(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n return message.split(\"\\n\")[0]?.trim() ?? message;\n}\n","/**\n * The `a11y:` key of the family config file (`manni.config.yaml`).\n *\n * Finding the file is the shared layer's job (src/shared/config-file.ts);\n * what the section may contain is decided here. The posture is meta's: an\n * unknown key or a wrong type is an error that names the file and the key,\n * because a misspelled `maxPages:` that was silently ignored would run the\n * crawl on defaults and read as a clean pass.\n */\nimport type { CollectionConfig } from \"../../shared/collections.js\";\nimport {\n findConfigFile,\n readConfigFile,\n type ConfigFileOptions,\n} from \"../../shared/config-file.js\";\nimport { A11yError, SEVERITIES, isSeverity, type Severity } from \"../types.js\";\nimport { MUST_START_WITH_SLASH, isUrlPathGlob } from \"./exclude.js\";\nimport { isHttpUrl } from \"./url.js\";\n\nexport interface A11yConfig {\n urls?: string[];\n crawl?: boolean;\n /** Cap on pages checked. Absent means no cap. */\n maxPages?: number;\n tags?: string[];\n severity?: Severity;\n timeout?: number;\n /** Globs matched against a URL's path; a match keeps the page out of the crawl. */\n exclude?: string[];\n}\n\nexport interface LoadedA11yConfig {\n config: A11yConfig;\n /** Where it came from, for messages. `null` when no file was found or `--no-config`. */\n source: string | null;\n /**\n * The family file's top-level `collections:`, passed straight through: a\n * collection's `url` is a seed source (proposal 0041, rule 12), and the\n * declaration belongs to the family rather than to `a11y:`. `[]` when no\n * file was found and when none are declared.\n */\n collections: CollectionConfig[];\n}\n\n/** The tool's key in the family file. */\nconst SECTION = \"a11y\";\n\n/**\n * The keys the section may carry. Adding a key to `A11yConfig` means adding\n * it here too, or a config using it is rejected; that coupling is the point.\n */\nconst CONFIG_KEYS = [\"urls\", \"crawl\", \"maxPages\", \"tags\", \"severity\", \"timeout\", \"exclude\"] as const;\n\nconst CONFIG_FILE: ConfigFileOptions = {\n section: SECTION,\n // The a11y tool never had a pre-family file of its own.\n legacyNames: [],\n toError: (message) => new A11yError(message),\n};\n\n/**\n * Discover (or read `explicitPath`) through the shared family-file loader with\n * `{ section: \"a11y\", legacyNames: [], toError: (m) => new A11yError(m) }`.\n * Unknown keys, wrong types, empty `urls` entries and non-`http(s)` urls are\n * `A11yError`s naming the file and key. `null` config (empty section) is `{}`.\n * The document's `collections:` come back alongside, parsed by the shared\n * loader: the key is the family's, not this section's.\n */\nexport async function loadA11yConfig(\n cwd: string,\n explicitPath?: string,\n): Promise<LoadedA11yConfig> {\n const file =\n explicitPath === undefined\n ? await findConfigFile(cwd, CONFIG_FILE)\n : await readConfigFile(explicitPath, cwd, CONFIG_FILE);\n if (file === null) return { config: {}, source: null, collections: [] };\n return {\n config: parseA11yConfig(file.value, file.source),\n source: file.source,\n collections: file.collections,\n };\n}\n\n/** Pure parser, exported for tests. */\nexport function parseA11yConfig(value: unknown, source: string): A11yConfig {\n if (value == null) return {};\n if (typeof value !== \"object\" || Array.isArray(value)) {\n throw new A11yError(`${source}: \\`${SECTION}:\\` must be a mapping.`);\n }\n const obj = value as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n if (!(CONFIG_KEYS as readonly string[]).includes(key)) {\n throw new A11yError(\n `${source}: \\`${SECTION}:\\` has unknown key \"${key}\". Supported keys: ${CONFIG_KEYS.join(\", \")}.`,\n );\n }\n }\n\n const config: A11yConfig = {};\n if (obj.urls !== undefined) config.urls = asUrls(obj.urls, source);\n if (obj.crawl !== undefined) config.crawl = asBoolean(obj.crawl, \"crawl\", source);\n if (obj.maxPages !== undefined) {\n config.maxPages = asPositiveInteger(obj.maxPages, \"maxPages\", source);\n }\n if (obj.tags !== undefined) config.tags = asStringList(obj.tags, \"tags\", source);\n if (obj.severity !== undefined) config.severity = asSeverity(obj.severity, source);\n if (obj.timeout !== undefined) {\n config.timeout = asPositiveInteger(obj.timeout, \"timeout\", source);\n }\n if (obj.exclude !== undefined) config.exclude = asExcludes(obj.exclude, source);\n return config;\n}\n\n/**\n * How a message names one entry of `a11y.exclude:`: the file it came from and\n * the entry's position in the list. Both the parse errors below and the\n * excluded-seed refusal in the check core name an entry this way, so a run\n * whose patterns came from config never mentions a flag the user did not type.\n */\nexport function excludeEntryLabel(source: string, index: number): string {\n return `${source}: \"${SECTION}.exclude[${String(index)}]\"`;\n}\n\n/**\n * Each entry has to be able to match a path, which always starts with `/`. A\n * pattern that cannot is a silent near-miss rather than a filter, so it is\n * refused here, naming whichever entry is wrong by its position in the list.\n */\nfunction asExcludes(value: unknown, source: string): string[] {\n const globs = asStringList(value, \"exclude\", source);\n globs.forEach((glob, i) => {\n const at = excludeEntryLabel(source, i);\n // An empty string fails the slash check too, so this is only about which\n // message it gets: \"must be a non-empty string\" names the mistake, where\n // \"must start with /\" would describe a pattern that is not there.\n if (glob === \"\") throw new A11yError(`${at} must be a non-empty string.`);\n if (!isUrlPathGlob(glob)) throw new A11yError(`${at} ${MUST_START_WITH_SLASH}`);\n });\n return globs;\n}\n\nfunction asStringList(value: unknown, field: string, source: string): string[] {\n if (!Array.isArray(value) || !value.every((v) => typeof v === \"string\")) {\n throw new A11yError(`${source}: \"${SECTION}.${field}\" must be a list of strings.`);\n }\n return value;\n}\n\nfunction asUrls(value: unknown, source: string): string[] {\n const urls = asStringList(value, \"urls\", source);\n urls.forEach((url, i) => {\n if (!isHttpUrl(url)) {\n throw new A11yError(\n `${source}: \"${SECTION}.urls[${i}]\" must be an http(s) URL, got \"${url}\".`,\n );\n }\n });\n return urls;\n}\n\nfunction asBoolean(value: unknown, field: string, source: string): boolean {\n if (typeof value !== \"boolean\") {\n throw new A11yError(`${source}: \"${SECTION}.${field}\" must be a boolean.`);\n }\n return value;\n}\n\nfunction asPositiveInteger(value: unknown, field: string, source: string): number {\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) {\n throw new A11yError(`${source}: \"${SECTION}.${field}\" must be an integer >= 1.`);\n }\n return value;\n}\n\nfunction asSeverity(value: unknown, source: string): Severity {\n if (typeof value !== \"string\" || !isSeverity(value)) {\n throw new A11yError(\n `${source}: \"${SECTION}.severity\" must be one of ${SEVERITIES.join(\", \")}.`,\n );\n }\n return value;\n}\n","/**\n * `--format github`: one workflow-command annotation per finding.\n *\n * There is no file to anchor to, since the check ran against a URL, so the\n * annotation carries the rule id as its title and names the page in the\n * message. Empty when the run is clean, so a clean CI step logs nothing.\n */\nimport {\n escapeWorkflowCommandMessage,\n escapeWorkflowCommandProperty,\n} from \"../../shared/github.js\";\nimport type { CheckRun, Severity } from \"../types.js\";\n\n/** The three levels a workflow command can carry. */\nexport type AnnotationLevel = \"error\" | \"warning\" | \"notice\";\n\n/**\n * The annotation level for a finding of the given severity.\n *\n * The family scale (`src/shared/severity.ts`) was chosen to match GitHub's,\n * so the map is one-to-one and this is the identity. It stays a function,\n * and stays exported, because it is the seam: the translation from a\n * domain's severity to an output's levels happens here and nowhere else,\n * and a reporter for a sink with another scale (SARIF's `note`) would carry\n * its own. axe's four impacts were folded onto these three by the analyzer\n * (`severityOf`), before any reporter saw the finding. Proposal 0035,\n * stress test 10.\n */\nexport function annotationLevel(severity: Severity): AnnotationLevel {\n return severity;\n}\n\nexport function renderGithub(run: CheckRun): string {\n const lines: string[] = [];\n for (const page of run.results) {\n if (page.error !== undefined) {\n // A page that did not load has no severity to translate. It is the most\n // serious thing a run can hold, so it is always an error.\n lines.push(\n `::error title=a11y/load::${escapeWorkflowCommandMessage(`${page.url}: ${page.error}`)}`,\n );\n continue;\n }\n for (const v of page.violations) {\n const n = v.nodes.length;\n const title = escapeWorkflowCommandProperty(`a11y/${v.id}`);\n const msg = escapeWorkflowCommandMessage(\n `${v.help} — ${n} node${n === 1 ? \"\" : \"s\"} on ${page.url} (${v.helpUrl})`,\n );\n lines.push(`::${annotationLevel(v.severity)} title=${title}::${msg}`);\n }\n }\n return lines.join(\"\\n\");\n}\n","/**\n * `--format json`: the `CheckRun` as it is, pretty-printed. Key order is the\n * order the types declare, so the envelope is stable from run to run.\n */\nimport type { CheckRun } from \"../types.js\";\n\nexport function renderJson(run: CheckRun): string {\n return JSON.stringify(run, null, 2);\n}\n","/**\n * `--format pretty`: the report a person reads in a terminal.\n *\n * One line per page, and under a failing page one line per rule followed by\n * the first three elements that failed it, each with axe's own statement of\n * which condition was not met. That per-element line is the deterministic\n * remediation: which rule, which selector, what to change. After the pages,\n * one `↪` line per page that redirected in the browser, hidden by `--quiet`\n * as a passing page is.\n *\n * Colors carry the meanings meta already gave them: ✓ green, ✗ red, URLs\n * cyan, and severity from dim (notice) through yellow (warning) to red\n * (error).\n *\n * Each violation line leads with the family severity, which is what the\n * floor and the exit code are decided on. axe's own word follows the rule id\n * in parentheses, `(axe: critical)`, so the line can still be matched\n * against the Deque rule page and the axe DevTools report that use it.\n */\nimport { palette, type Colors } from \"../../shared/color.js\";\nimport {\n SEVERITIES,\n type CheckRun,\n type Severity,\n type PageResult,\n type Violation,\n} from \"../types.js\";\nimport type { RenderOptions } from \"./index.js\";\n\n/** How many failing elements are listed under one rule before `(+N more)`. */\nconst MAX_NODES = 3;\n\nexport function renderPretty(run: CheckRun, opts: RenderOptions): string {\n const c = palette(opts.color);\n const { summary } = run;\n const lines: string[] = [];\n\n lines.push(`Checked ${summary.checked} of ${summary.discovered} pages (${where(run, c)})`);\n\n for (const page of run.results) {\n if (page.error !== undefined) {\n lines.push(`${c.red(\"✗\")} ${c.cyan(page.url)} could not load: ${page.error}`);\n continue;\n }\n if (page.violations.length === 0) {\n if (!opts.quiet) lines.push(`${c.green(\"✓\")} ${c.cyan(page.url)} score ${scoreText(page)}`);\n continue;\n }\n lines.push(\n `${c.red(\"✗\")} ${c.cyan(page.url)} score ${scoreText(page)} ${severityCounts(page.violations, c)}`,\n );\n for (const v of page.violations) lines.push(...violationLines(v, c));\n }\n\n if (!opts.quiet) {\n for (const r of run.redirects) {\n lines.push(`${c.dim(\"↪\")} ${c.cyan(r.url)} redirects to ${c.cyan(r.to)}`);\n }\n }\n\n const failed = summary.failed;\n const footer =\n `${summary.violations} violation${summary.violations === 1 ? \"\" : \"s\"} on ${failed} of ${summary.checked} pages` +\n (summary.skipped > 0 ? `; ${summary.skipped} skipped (--max-pages)` : \"\") +\n (summary.duplicates > 0\n ? `; ${summary.duplicates} duplicate${summary.duplicates === 1 ? \"\" : \"s\"} dropped`\n : \"\") +\n (summary.redirects > 0\n ? `; ${summary.redirects} redirect${summary.redirects === 1 ? \"\" : \"s\"}`\n : \"\") +\n (summary.excluded > 0 ? `; ${summary.excluded} excluded` : \"\");\n lines.push(\"\");\n lines.push(failed > 0 ? c.red(footer) : c.green(footer));\n return lines.join(\"\\n\");\n}\n\n/**\n * How the pages were found. `--no-crawl` never looks for a sitemap, so a null\n * `sitemap` alone cannot tell \"no crawl\" from \"crawled, found no sitemap\".\n *\n * A sitemap that was found is not a sitemap that supplied pages. One whose\n * every `<loc>` is on another host parses and yields nothing, which is what a\n * built sitemap does when the built site is served on a local preview. So a\n * named sitemap carries its count whenever links did some of the work, and\n * the run that was wholly a sitemap's says so with the URL alone.\n */\nfunction where(run: CheckRun, c: Colors): string {\n const { summary } = run;\n if (!summary.crawl) return \"no crawl\";\n if (summary.sitemap === null) return \"no sitemap; followed links\";\n const found = `sitemap: ${c.cyan(summary.sitemap)}`;\n const n = summary.sitemapPages;\n if (n === 0) return `${found}, 0 pages; followed links`;\n if (n >= summary.discovered) return found;\n return `${found}, ${n} page${n === 1 ? \"\" : \"s\"}; followed links for the rest`;\n}\n\nfunction scoreText(page: PageResult): string {\n return page.score === null ? \"n/a\" : String(page.score);\n}\n\n/** `2 errors, 1 notice`: most severe first, pluralised, zeros omitted. */\nfunction severityCounts(violations: Violation[], c: Colors): string {\n const counts: Record<Severity, number> = { notice: 0, warning: 0, error: 0 };\n for (const v of violations) counts[v.severity] += 1;\n return [...SEVERITIES]\n .reverse()\n .filter((severity) => counts[severity] > 0)\n .map((severity) => {\n const n = counts[severity];\n return paintSeverity(`${n} ${severity}${n === 1 ? \"\" : \"s\"}`, severity, c);\n })\n .join(\", \");\n}\n\nfunction violationLines(v: Violation, c: Colors): string[] {\n const n = v.nodes.length;\n const lines = [\n ` ${paintSeverity(v.severity, v.severity, c)} ${v.id} ${c.dim(`(axe: ${v.impact})`)} ${n} node${n === 1 ? \"\" : \"s\"} ${v.help} ${c.dim(v.helpUrl)}`,\n ];\n for (const node of v.nodes.slice(0, MAX_NODES)) {\n lines.push(` ${node.target} → ${oneLine(node.summary)}`);\n }\n if (n > MAX_NODES) lines.push(c.dim(` (+${n - MAX_NODES} more)`));\n return lines;\n}\n\n/**\n * axe's failureSummary is a heading line followed by one indented line per\n * unmet condition. On one line: the heading, then the conditions joined by\n * `; `.\n */\nfunction oneLine(summary: string): string {\n const [head, ...rest] = summary\n .split(\"\\n\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (head === undefined) return \"\";\n return rest.length === 0 ? head : `${head} ${rest.join(\"; \")}`;\n}\n\nfunction paintSeverity(text: string, severity: Severity, c: Colors): string {\n switch (severity) {\n case \"notice\":\n return c.dim(text);\n case \"warning\":\n return c.yellow(text);\n case \"error\":\n return c.red(text);\n }\n}\n","/**\n * The a11y reporters render a `CheckRun` to a string. The command layer writes\n * the result to stdout; diagnostics go to stderr separately.\n */\nimport { A11yError, type CheckRun } from \"../types.js\";\nimport { renderGithub } from \"./github.js\";\nimport { renderJson } from \"./json.js\";\nimport { renderPretty } from \"./pretty.js\";\n\nexport { renderGithub, annotationLevel } from \"./github.js\";\nexport type { AnnotationLevel } from \"./github.js\";\nexport { renderJson } from \"./json.js\";\nexport { renderPretty } from \"./pretty.js\";\n\n/**\n * Every value `--format` accepts, in the order help and error messages list\n * them. Stated once, so the union, the guard, the option text and the error\n * cannot drift apart. SARIF and JUnit are deferred (proposal 0035).\n */\nexport const A11Y_FORMATS = [\"pretty\", \"json\", \"github\"] as const;\nexport type A11yFormat = (typeof A11Y_FORMATS)[number];\n\n/** `\"pretty | json | github\"`, for messages and help text. */\nexport const A11Y_FORMAT_LIST: string = A11Y_FORMATS.join(\" | \");\n\nexport function isA11yFormat(value: string): value is A11yFormat {\n return (A11Y_FORMATS as readonly string[]).includes(value);\n}\n\nexport interface RenderOptions {\n /** From shared `shouldColor`. */\n color: boolean;\n /** pretty only: hide pages with no remaining violations. */\n quiet: boolean;\n}\n\n/** Returns the full text without a trailing newline; `github` returns \"\" when clean. */\nexport function render(format: A11yFormat, run: CheckRun, opts: RenderOptions): string {\n switch (format) {\n case \"pretty\":\n return renderPretty(run, opts);\n case \"json\":\n return renderJson(run);\n case \"github\":\n return renderGithub(run);\n default: {\n // Exhaustive: a format added to `A11Y_FORMATS` without a case here is a\n // compile error; the throw is for a caller who bypassed the guard.\n const unreachable: never = format;\n throw new A11yError(\n `Unknown report format ${JSON.stringify(unreachable)}. Use ${A11Y_FORMAT_LIST}.`,\n );\n }\n }\n}\n","/**\n * Progress on stderr, while the crawl runs.\n *\n * The browser launch alone takes seconds and a fifty-page site takes a\n * minute, and a command that says nothing for that long reads as hung. So\n * the run narrates itself on stderr, where a diagnostic belongs; stdout stays\n * the report, and `-f json | jq` keeps working.\n *\n * Two renderings. On a terminal, one status line rewritten in place\n * (`\\r`, erase line, then the text, no newline) and cleared on `done`, so the\n * report on stdout is not preceded by a dangling line. Off a terminal, one\n * plain line per event, newline-terminated and prefixed the way `warn()`\n * writes, with no escape codes, since a CI log keeps every line.\n *\n * Colours keep the meanings meta gave them: ✓ green, ✗ red, URLs cyan, the\n * counter dim. They apply on a terminal only.\n */\nimport { palette, type Colors } from \"../../shared/color.js\";\nimport { programName } from \"../../shared/program-name.js\";\nimport type { ProgressEvent, ProgressListener } from \"../types.js\";\n\nexport interface ProgressOptions {\n /** Where to write: `process.stderr`, or a fake in tests. */\n stream: { write(s: string): boolean; isTTY?: boolean; columns?: number };\n /** Paint the status line. Ignored off a terminal. */\n color: boolean;\n /** Rewrite one line in place (true) or append a line per event (false). */\n tty: boolean;\n}\n\n/** Carriage return, then erase the whole line: the start of every status line. */\nexport const CLEAR_LINE = \"\\r\\x1b[2K\";\n\n/** The width a status line is fitted to when the stream does not say. */\nconst DEFAULT_COLUMNS = 80;\n\n/** Returns a listener that renders events to `stream`. */\nexport function createProgressReporter(opts: ProgressOptions): ProgressListener {\n return opts.tty ? terminalReporter(opts) : plainReporter(opts.stream);\n}\n\nfunction plural(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`;\n}\n\n/** `[3/51]`, from the last `page` event; `checked` carries no `queued` of its own. */\nfunction counter(index: number, queued: number): string {\n return `[${index}/${queued}]`;\n}\n\nfunction terminalReporter(opts: ProgressOptions): ProgressListener {\n const { stream } = opts;\n const c = palette(opts.color);\n let queued = 0;\n\n const show = (text: string): void => {\n stream.write(`${CLEAR_LINE}${text}`);\n };\n /** `prefix`, the URL, then `suffix`; the URL shortened in the middle to fit the line. */\n const showUrl = (prefix: string, url: string, suffix = \"\"): void => {\n const columns = stream.columns ?? DEFAULT_COLUMNS;\n const fitted = truncateMiddle(url, columns - visibleLength(prefix) - suffix.length);\n show(`${prefix}${c.cyan(fitted)}${suffix}`);\n };\n\n return (event: ProgressEvent): void => {\n switch (event.kind) {\n case \"browser\":\n show(\"Starting browser…\");\n return;\n case \"sitemap\":\n if (event.source === null) show(\"No sitemap; following links\");\n else showUrl(\"Sitemap: \", event.source, ` (${plural(event.urls, \"page\")})`);\n return;\n case \"excluded\":\n show(`Excluded ${plural(event.urls, \"page\")} (${plural(event.patterns, \"pattern\")})`);\n return;\n case \"page\":\n queued = event.queued;\n showUrl(`${c.dim(counter(event.index, queued))} `, event.url);\n return;\n case \"checked\":\n showUrl(`${c.dim(counter(event.index, queued))} ${outcome(event, c)}`, event.url);\n return;\n case \"redirected\":\n showUrl(`${c.dim(counter(event.index, queued))} ${c.dim(\"↪\")} redirects to `, event.to);\n return;\n case \"done\":\n stream.write(CLEAR_LINE);\n return;\n }\n };\n}\n\n/** `✓ `, `✗ 2 violations `, or `✗ could not load: … `: what goes between the counter and the URL. */\nfunction outcome(event: Extract<ProgressEvent, { kind: \"checked\" }>, c: Colors): string {\n if (event.error !== undefined) return `${c.red(\"✗\")} could not load: ${event.error} `;\n if (event.violations === 0) return `${c.green(\"✓\")} `;\n return `${c.red(\"✗\")} ${plural(event.violations, \"violation\")} `;\n}\n\nfunction plainReporter(stream: ProgressOptions[\"stream\"]): ProgressListener {\n let queued = 0;\n const say = (text: string): void => {\n stream.write(`${programName()}: ${text}\\n`);\n };\n\n return (event: ProgressEvent): void => {\n switch (event.kind) {\n case \"browser\":\n say(\"starting browser\");\n return;\n case \"sitemap\":\n if (event.source === null) say(\"no sitemap; following links\");\n else say(`sitemap ${event.source} (${plural(event.urls, \"page\")})`);\n return;\n case \"excluded\":\n say(`excluded ${plural(event.urls, \"page\")} (${plural(event.patterns, \"pattern\")})`);\n return;\n case \"page\":\n queued = event.queued;\n say(`${counter(event.index, queued)} ${event.url}`);\n return;\n case \"checked\": {\n const head = counter(event.index, queued);\n if (event.error !== undefined) say(`${head} could not load: ${event.error}`);\n else if (event.violations === 0) say(`${head} ✓`);\n else say(`${head} ✗ ${plural(event.violations, \"violation\")}`);\n return;\n }\n case \"redirected\":\n say(`${counter(event.index, queued)} ↪ redirects to ${event.to}`);\n return;\n case \"done\":\n say(`checked ${plural(event.checked, \"page\")}, ${event.skipped} skipped`);\n return;\n }\n };\n}\n\n/** Length as the terminal shows it, with ANSI sequences taken out. */\nfunction visibleLength(text: string): number {\n return text.replace(/\\x1b\\[[0-9;]*m/g, \"\").length;\n}\n\n/**\n * Shorten `text` to `room` characters by replacing its middle with `…`, so\n * both the host and the page's own name survive. Never shorter than a few\n * characters, however narrow the terminal.\n */\nfunction truncateMiddle(text: string, room: number): string {\n const max = Math.max(room, 8);\n if (text.length <= max) return text;\n const head = Math.ceil((max - 1) / 2);\n const tail = max - 1 - head;\n return `${text.slice(0, head)}…${text.slice(text.length - tail)}`;\n}\n"],"mappings":"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","names":["outcome","outcome","crawl"]}
|